diff --git a/Assignments/.gitignore b/Assignments/MP1_CalebFontenot/.gitignore
similarity index 100%
rename from Assignments/.gitignore
rename to Assignments/MP1_CalebFontenot/.gitignore
diff --git a/Assignments/.idea/.gitignore b/Assignments/MP1_CalebFontenot/.idea/.gitignore
similarity index 100%
rename from Assignments/.idea/.gitignore
rename to Assignments/MP1_CalebFontenot/.idea/.gitignore
diff --git a/Assignments/.idea/compiler.xml b/Assignments/MP1_CalebFontenot/.idea/compiler.xml
similarity index 100%
rename from Assignments/.idea/compiler.xml
rename to Assignments/MP1_CalebFontenot/.idea/compiler.xml
diff --git a/Assignments/.idea/deploymentTargetDropDown.xml b/Assignments/MP1_CalebFontenot/.idea/deploymentTargetDropDown.xml
similarity index 100%
rename from Assignments/.idea/deploymentTargetDropDown.xml
rename to Assignments/MP1_CalebFontenot/.idea/deploymentTargetDropDown.xml
diff --git a/Assignments/.idea/discord.xml b/Assignments/MP1_CalebFontenot/.idea/discord.xml
similarity index 100%
rename from Assignments/.idea/discord.xml
rename to Assignments/MP1_CalebFontenot/.idea/discord.xml
diff --git a/Assignments/.idea/gradle.xml b/Assignments/MP1_CalebFontenot/.idea/gradle.xml
similarity index 100%
rename from Assignments/.idea/gradle.xml
rename to Assignments/MP1_CalebFontenot/.idea/gradle.xml
diff --git a/Assignments/.idea/migrations.xml b/Assignments/MP1_CalebFontenot/.idea/migrations.xml
similarity index 100%
rename from Assignments/.idea/migrations.xml
rename to Assignments/MP1_CalebFontenot/.idea/migrations.xml
diff --git a/Assignments/.idea/misc.xml b/Assignments/MP1_CalebFontenot/.idea/misc.xml
similarity index 100%
rename from Assignments/.idea/misc.xml
rename to Assignments/MP1_CalebFontenot/.idea/misc.xml
diff --git a/Assignments/MP1_CalebFontenot/.idea/vcs.xml b/Assignments/MP1_CalebFontenot/.idea/vcs.xml
new file mode 100644
index 0000000..b2bdec2
--- /dev/null
+++ b/Assignments/MP1_CalebFontenot/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Assignments/MP1_CalebFontenot/AndroidProgramming3e.zip b/Assignments/MP1_CalebFontenot/AndroidProgramming3e.zip
new file mode 100644
index 0000000..146cb80
Binary files /dev/null and b/Assignments/MP1_CalebFontenot/AndroidProgramming3e.zip differ
diff --git a/Assignments/Test1/.gitignore b/Assignments/MP1_CalebFontenot/Test1/.gitignore
similarity index 100%
rename from Assignments/Test1/.gitignore
rename to Assignments/MP1_CalebFontenot/Test1/.gitignore
diff --git a/Assignments/Test1/.idea/.gitignore b/Assignments/MP1_CalebFontenot/Test1/.idea/.gitignore
similarity index 100%
rename from Assignments/Test1/.idea/.gitignore
rename to Assignments/MP1_CalebFontenot/Test1/.idea/.gitignore
diff --git a/Assignments/Test1/.idea/compiler.xml b/Assignments/MP1_CalebFontenot/Test1/.idea/compiler.xml
similarity index 100%
rename from Assignments/Test1/.idea/compiler.xml
rename to Assignments/MP1_CalebFontenot/Test1/.idea/compiler.xml
diff --git a/Assignments/Test1/.idea/deploymentTargetDropDown.xml b/Assignments/MP1_CalebFontenot/Test1/.idea/deploymentTargetDropDown.xml
similarity index 100%
rename from Assignments/Test1/.idea/deploymentTargetDropDown.xml
rename to Assignments/MP1_CalebFontenot/Test1/.idea/deploymentTargetDropDown.xml
diff --git a/Assignments/Test1/.idea/gradle.xml b/Assignments/MP1_CalebFontenot/Test1/.idea/gradle.xml
similarity index 100%
rename from Assignments/Test1/.idea/gradle.xml
rename to Assignments/MP1_CalebFontenot/Test1/.idea/gradle.xml
diff --git a/Assignments/Test1/.idea/misc.xml b/Assignments/MP1_CalebFontenot/Test1/.idea/misc.xml
similarity index 100%
rename from Assignments/Test1/.idea/misc.xml
rename to Assignments/MP1_CalebFontenot/Test1/.idea/misc.xml
diff --git a/Assignments/Test1/app/.gitignore b/Assignments/MP1_CalebFontenot/Test1/app/.gitignore
similarity index 100%
rename from Assignments/Test1/app/.gitignore
rename to Assignments/MP1_CalebFontenot/Test1/app/.gitignore
diff --git a/Assignments/Test1/app/build.gradle b/Assignments/MP1_CalebFontenot/Test1/app/build.gradle
similarity index 100%
rename from Assignments/Test1/app/build.gradle
rename to Assignments/MP1_CalebFontenot/Test1/app/build.gradle
diff --git a/Assignments/Test1/app/proguard-rules.pro b/Assignments/MP1_CalebFontenot/Test1/app/proguard-rules.pro
similarity index 100%
rename from Assignments/Test1/app/proguard-rules.pro
rename to Assignments/MP1_CalebFontenot/Test1/app/proguard-rules.pro
diff --git a/Assignments/Test1/app/src/androidTest/java/com/calebfontenot/test1/ExampleInstrumentedTest.java b/Assignments/MP1_CalebFontenot/Test1/app/src/androidTest/java/com/calebfontenot/test1/ExampleInstrumentedTest.java
similarity index 100%
rename from Assignments/Test1/app/src/androidTest/java/com/calebfontenot/test1/ExampleInstrumentedTest.java
rename to Assignments/MP1_CalebFontenot/Test1/app/src/androidTest/java/com/calebfontenot/test1/ExampleInstrumentedTest.java
diff --git a/Assignments/Test1/app/src/main/AndroidManifest.xml b/Assignments/MP1_CalebFontenot/Test1/app/src/main/AndroidManifest.xml
similarity index 100%
rename from Assignments/Test1/app/src/main/AndroidManifest.xml
rename to Assignments/MP1_CalebFontenot/Test1/app/src/main/AndroidManifest.xml
diff --git a/Assignments/Test1/app/src/main/java/com/calebfontenot/test1/HelloWorld.java b/Assignments/MP1_CalebFontenot/Test1/app/src/main/java/com/calebfontenot/test1/HelloWorld.java
similarity index 100%
rename from Assignments/Test1/app/src/main/java/com/calebfontenot/test1/HelloWorld.java
rename to Assignments/MP1_CalebFontenot/Test1/app/src/main/java/com/calebfontenot/test1/HelloWorld.java
diff --git a/Assignments/Test1/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/Assignments/MP1_CalebFontenot/Test1/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
similarity index 100%
rename from Assignments/Test1/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
rename to Assignments/MP1_CalebFontenot/Test1/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
diff --git a/Assignments/Test1/app/src/main/res/drawable/ic_launcher_background.xml b/Assignments/MP1_CalebFontenot/Test1/app/src/main/res/drawable/ic_launcher_background.xml
similarity index 100%
rename from Assignments/Test1/app/src/main/res/drawable/ic_launcher_background.xml
rename to Assignments/MP1_CalebFontenot/Test1/app/src/main/res/drawable/ic_launcher_background.xml
diff --git a/Assignments/Test1/app/src/main/res/layout/activity_hello.xml b/Assignments/MP1_CalebFontenot/Test1/app/src/main/res/layout/activity_hello.xml
similarity index 100%
rename from Assignments/Test1/app/src/main/res/layout/activity_hello.xml
rename to Assignments/MP1_CalebFontenot/Test1/app/src/main/res/layout/activity_hello.xml
diff --git a/Assignments/Test1/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/Assignments/MP1_CalebFontenot/Test1/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
similarity index 100%
rename from Assignments/Test1/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
rename to Assignments/MP1_CalebFontenot/Test1/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
diff --git a/Assignments/Test1/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/Assignments/MP1_CalebFontenot/Test1/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
similarity index 100%
rename from Assignments/Test1/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
rename to Assignments/MP1_CalebFontenot/Test1/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
diff --git a/Assignments/Test1/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/Assignments/MP1_CalebFontenot/Test1/app/src/main/res/mipmap-hdpi/ic_launcher.webp
similarity index 100%
rename from Assignments/Test1/app/src/main/res/mipmap-hdpi/ic_launcher.webp
rename to Assignments/MP1_CalebFontenot/Test1/app/src/main/res/mipmap-hdpi/ic_launcher.webp
diff --git a/Assignments/Test1/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/Assignments/MP1_CalebFontenot/Test1/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
similarity index 100%
rename from Assignments/Test1/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
rename to Assignments/MP1_CalebFontenot/Test1/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
diff --git a/Assignments/Test1/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/Assignments/MP1_CalebFontenot/Test1/app/src/main/res/mipmap-mdpi/ic_launcher.webp
similarity index 100%
rename from Assignments/Test1/app/src/main/res/mipmap-mdpi/ic_launcher.webp
rename to Assignments/MP1_CalebFontenot/Test1/app/src/main/res/mipmap-mdpi/ic_launcher.webp
diff --git a/Assignments/Test1/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/Assignments/MP1_CalebFontenot/Test1/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
similarity index 100%
rename from Assignments/Test1/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
rename to Assignments/MP1_CalebFontenot/Test1/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
diff --git a/Assignments/Test1/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/Assignments/MP1_CalebFontenot/Test1/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
similarity index 100%
rename from Assignments/Test1/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
rename to Assignments/MP1_CalebFontenot/Test1/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
diff --git a/Assignments/Test1/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/Assignments/MP1_CalebFontenot/Test1/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
similarity index 100%
rename from Assignments/Test1/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
rename to Assignments/MP1_CalebFontenot/Test1/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
diff --git a/Assignments/Test1/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/Assignments/MP1_CalebFontenot/Test1/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
similarity index 100%
rename from Assignments/Test1/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
rename to Assignments/MP1_CalebFontenot/Test1/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
diff --git a/Assignments/Test1/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/Assignments/MP1_CalebFontenot/Test1/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
similarity index 100%
rename from Assignments/Test1/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
rename to Assignments/MP1_CalebFontenot/Test1/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
diff --git a/Assignments/Test1/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/Assignments/MP1_CalebFontenot/Test1/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
similarity index 100%
rename from Assignments/Test1/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
rename to Assignments/MP1_CalebFontenot/Test1/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
diff --git a/Assignments/Test1/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/Assignments/MP1_CalebFontenot/Test1/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
similarity index 100%
rename from Assignments/Test1/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
rename to Assignments/MP1_CalebFontenot/Test1/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
diff --git a/Assignments/Test1/app/src/main/res/values-night/themes.xml b/Assignments/MP1_CalebFontenot/Test1/app/src/main/res/values-night/themes.xml
similarity index 100%
rename from Assignments/Test1/app/src/main/res/values-night/themes.xml
rename to Assignments/MP1_CalebFontenot/Test1/app/src/main/res/values-night/themes.xml
diff --git a/Assignments/Test1/app/src/main/res/values/colors.xml b/Assignments/MP1_CalebFontenot/Test1/app/src/main/res/values/colors.xml
similarity index 100%
rename from Assignments/Test1/app/src/main/res/values/colors.xml
rename to Assignments/MP1_CalebFontenot/Test1/app/src/main/res/values/colors.xml
diff --git a/Assignments/Test1/app/src/main/res/values/strings.xml b/Assignments/MP1_CalebFontenot/Test1/app/src/main/res/values/strings.xml
similarity index 100%
rename from Assignments/Test1/app/src/main/res/values/strings.xml
rename to Assignments/MP1_CalebFontenot/Test1/app/src/main/res/values/strings.xml
diff --git a/Assignments/Test1/app/src/main/res/values/themes.xml b/Assignments/MP1_CalebFontenot/Test1/app/src/main/res/values/themes.xml
similarity index 100%
rename from Assignments/Test1/app/src/main/res/values/themes.xml
rename to Assignments/MP1_CalebFontenot/Test1/app/src/main/res/values/themes.xml
diff --git a/Assignments/Test1/app/src/main/res/xml/backup_rules.xml b/Assignments/MP1_CalebFontenot/Test1/app/src/main/res/xml/backup_rules.xml
similarity index 100%
rename from Assignments/Test1/app/src/main/res/xml/backup_rules.xml
rename to Assignments/MP1_CalebFontenot/Test1/app/src/main/res/xml/backup_rules.xml
diff --git a/Assignments/Test1/app/src/main/res/xml/data_extraction_rules.xml b/Assignments/MP1_CalebFontenot/Test1/app/src/main/res/xml/data_extraction_rules.xml
similarity index 100%
rename from Assignments/Test1/app/src/main/res/xml/data_extraction_rules.xml
rename to Assignments/MP1_CalebFontenot/Test1/app/src/main/res/xml/data_extraction_rules.xml
diff --git a/Assignments/Test1/app/src/test/java/com/calebfontenot/test1/ExampleUnitTest.java b/Assignments/MP1_CalebFontenot/Test1/app/src/test/java/com/calebfontenot/test1/ExampleUnitTest.java
similarity index 100%
rename from Assignments/Test1/app/src/test/java/com/calebfontenot/test1/ExampleUnitTest.java
rename to Assignments/MP1_CalebFontenot/Test1/app/src/test/java/com/calebfontenot/test1/ExampleUnitTest.java
diff --git a/Assignments/Test1/build.gradle b/Assignments/MP1_CalebFontenot/Test1/build.gradle
similarity index 100%
rename from Assignments/Test1/build.gradle
rename to Assignments/MP1_CalebFontenot/Test1/build.gradle
diff --git a/Assignments/Test1/gradle.properties b/Assignments/MP1_CalebFontenot/Test1/gradle.properties
similarity index 100%
rename from Assignments/Test1/gradle.properties
rename to Assignments/MP1_CalebFontenot/Test1/gradle.properties
diff --git a/Assignments/Test1/gradle/wrapper/gradle-wrapper.jar b/Assignments/MP1_CalebFontenot/Test1/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
rename from Assignments/Test1/gradle/wrapper/gradle-wrapper.jar
rename to Assignments/MP1_CalebFontenot/Test1/gradle/wrapper/gradle-wrapper.jar
diff --git a/Assignments/Test1/gradle/wrapper/gradle-wrapper.properties b/Assignments/MP1_CalebFontenot/Test1/gradle/wrapper/gradle-wrapper.properties
similarity index 100%
rename from Assignments/Test1/gradle/wrapper/gradle-wrapper.properties
rename to Assignments/MP1_CalebFontenot/Test1/gradle/wrapper/gradle-wrapper.properties
diff --git a/Assignments/Test1/gradlew b/Assignments/MP1_CalebFontenot/Test1/gradlew
similarity index 100%
rename from Assignments/Test1/gradlew
rename to Assignments/MP1_CalebFontenot/Test1/gradlew
diff --git a/Assignments/Test1/gradlew.bat b/Assignments/MP1_CalebFontenot/Test1/gradlew.bat
similarity index 100%
rename from Assignments/Test1/gradlew.bat
rename to Assignments/MP1_CalebFontenot/Test1/gradlew.bat
diff --git a/Assignments/Test1/settings.gradle b/Assignments/MP1_CalebFontenot/Test1/settings.gradle
similarity index 100%
rename from Assignments/Test1/settings.gradle
rename to Assignments/MP1_CalebFontenot/Test1/settings.gradle
diff --git a/Assignments/app/.gitignore b/Assignments/MP1_CalebFontenot/app/.gitignore
similarity index 100%
rename from Assignments/app/.gitignore
rename to Assignments/MP1_CalebFontenot/app/.gitignore
diff --git a/Assignments/app/build.gradle b/Assignments/MP1_CalebFontenot/app/build.gradle
similarity index 100%
rename from Assignments/app/build.gradle
rename to Assignments/MP1_CalebFontenot/app/build.gradle
diff --git a/Assignments/app/proguard-rules.pro b/Assignments/MP1_CalebFontenot/app/proguard-rules.pro
similarity index 100%
rename from Assignments/app/proguard-rules.pro
rename to Assignments/MP1_CalebFontenot/app/proguard-rules.pro
diff --git a/Assignments/app/src/androidTest/java/com/calebfontenot/quizchapter2/ExampleInstrumentedTest.java b/Assignments/MP1_CalebFontenot/app/src/androidTest/java/com/calebfontenot/quizchapter2/ExampleInstrumentedTest.java
similarity index 100%
rename from Assignments/app/src/androidTest/java/com/calebfontenot/quizchapter2/ExampleInstrumentedTest.java
rename to Assignments/MP1_CalebFontenot/app/src/androidTest/java/com/calebfontenot/quizchapter2/ExampleInstrumentedTest.java
diff --git a/Assignments/app/src/main/AndroidManifest.xml b/Assignments/MP1_CalebFontenot/app/src/main/AndroidManifest.xml
similarity index 100%
rename from Assignments/app/src/main/AndroidManifest.xml
rename to Assignments/MP1_CalebFontenot/app/src/main/AndroidManifest.xml
diff --git a/Assignments/MP1_CalebFontenot/app/src/main/java/com/calebfontenot/quizchapter2/MainActivity.java b/Assignments/MP1_CalebFontenot/app/src/main/java/com/calebfontenot/quizchapter2/MainActivity.java
new file mode 100644
index 0000000..9796055
--- /dev/null
+++ b/Assignments/MP1_CalebFontenot/app/src/main/java/com/calebfontenot/quizchapter2/MainActivity.java
@@ -0,0 +1,142 @@
+package com.calebfontenot.quizchapter2;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class MainActivity extends AppCompatActivity {
+ private Button trueButton;
+ private Button falseButton;
+ private Button nextButton;
+ private Button previousButton;
+ private Button resetButton;
+ private TextView mQuestionTextView;
+ private TextView mScoreTextView;
+ private final String TAG = "QuizController";
+ private long answersCorrect = 0, answersIncorrect = 0;
+ private Question[] mQuestionBank = new Question[]
+ {
+ new Question(R.string.question_australia, true),
+ new Question(R.string.question_oceans, true),
+ new Question(R.string.question_mideast, false),
+ new Question(R.string.question_africa, false),
+ new Question(R.string.question_asia, true),
+ };
+ private int mCurrentIndex = 0;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ Log.i(TAG,"Calling onCreate...");
+ mQuestionTextView = (TextView)
+ findViewById(R.id.question_text_view);
+ int question =
+ mQuestionBank[mCurrentIndex].getmTextResId();
+ //mQuestionTextView.setText(question);
+
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ mQuestionTextView = findViewById(R.id.question_text_view);
+ trueButton = findViewById(R.id.true_button);
+ falseButton = findViewById(R.id.false_button);
+ nextButton = findViewById(R.id.next_button);
+ previousButton = findViewById(R.id.previous_button);
+ mScoreTextView = findViewById(R.id.score_counter);
+ resetButton = findViewById(R.id.reset_score_button);
+ updateScores();
+
+ trueButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Log.i(TAG,"Calling trueButton event listener");
+ Question q = mQuestionBank[MainActivity.this.mCurrentIndex];
+ if (q.ismAnswerTrue()) {
+ Toast.makeText(MainActivity.this, R.string.correct_toast, Toast.LENGTH_SHORT).show();
+ answersCorrect++;
+ updateScores();
+ } else {
+ Toast.makeText(MainActivity.this, R.string.incorrect_toast, Toast.LENGTH_SHORT).show();
+ answersIncorrect++;
+ updateScores();
+ }
+ }
+ });
+
+ falseButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Log.i(TAG,"Calling falseButton event listener");
+ Question q = mQuestionBank[MainActivity.this.mCurrentIndex];
+ if (!q.ismAnswerTrue()) {
+ Toast.makeText(MainActivity.this, R.string.correct_toast, Toast.LENGTH_SHORT).show();
+ answersCorrect++;
+ updateScores();
+ } else {
+ Toast.makeText(MainActivity.this, R.string.incorrect_toast, Toast.LENGTH_SHORT).show();
+ answersIncorrect++;
+ updateScores();
+ }
+ }
+ });
+
+ View.OnClickListener nextQuestionHandler = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Log.i(TAG,"Calling nextButton event listener");
+ updateScores();
+ Question q = mQuestionBank[MainActivity.this.mCurrentIndex];
+ int idOfQuestion = q.getmTextResId();
+ if(MainActivity.this.mCurrentIndex == mQuestionBank.length - 1) {
+ MainActivity.this.mCurrentIndex = 0;
+ } else {
+ MainActivity.this.mCurrentIndex++;
+ }
+ Log.i(TAG, "mCurrentIndex is: " + mCurrentIndex);
+ MainActivity.this.mQuestionTextView.setText(idOfQuestion);
+ }
+ };
+
+ nextButton.setOnClickListener(nextQuestionHandler);
+ mQuestionTextView.setOnClickListener(nextQuestionHandler);
+
+
+ previousButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Log.i(TAG, "Calling previousButton event listener");
+ updateScores();
+ Question q = mQuestionBank[MainActivity.this.mCurrentIndex];
+ int idOfQuestion = q.getmTextResId();
+ if (MainActivity.this.mCurrentIndex <= 0) {
+ MainActivity.this.mCurrentIndex = mQuestionBank.length - 1;
+ } else {
+ MainActivity.this.mCurrentIndex--;
+ }
+ Log.i(TAG, "mCurrentIndex is: " + mCurrentIndex);
+ MainActivity.this.mQuestionTextView.setText(idOfQuestion);
+ }
+ });
+
+ resetButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Log.i(TAG, "Resetting scores!");
+ answersCorrect = answersIncorrect = 0;
+ updateScores();
+ }
+ });
+ }
+
+ private void updateScores() {
+ String score = "Answers correct: " + answersCorrect + "\n"
+ + "Answers incorrect: " + answersIncorrect + "\n"
+ + "Question number: " + (MainActivity.this.mCurrentIndex + 1) + " out of " + mQuestionBank.length;
+ Log.i(TAG, score);
+ MainActivity.this.mScoreTextView.setText(score);
+ }
+
+}
\ No newline at end of file
diff --git a/Assignments/app/src/main/java/com/calebfontenot/quizchapter2/Question.java b/Assignments/MP1_CalebFontenot/app/src/main/java/com/calebfontenot/quizchapter2/Question.java
similarity index 100%
rename from Assignments/app/src/main/java/com/calebfontenot/quizchapter2/Question.java
rename to Assignments/MP1_CalebFontenot/app/src/main/java/com/calebfontenot/quizchapter2/Question.java
diff --git a/Assignments/MP1_CalebFontenot/app/src/main/res/drawable-hdpi/arrow_left.png b/Assignments/MP1_CalebFontenot/app/src/main/res/drawable-hdpi/arrow_left.png
new file mode 100644
index 0000000..60186ca
Binary files /dev/null and b/Assignments/MP1_CalebFontenot/app/src/main/res/drawable-hdpi/arrow_left.png differ
diff --git a/Assignments/MP1_CalebFontenot/app/src/main/res/drawable-hdpi/arrow_right.png b/Assignments/MP1_CalebFontenot/app/src/main/res/drawable-hdpi/arrow_right.png
new file mode 100644
index 0000000..5eb216a
Binary files /dev/null and b/Assignments/MP1_CalebFontenot/app/src/main/res/drawable-hdpi/arrow_right.png differ
diff --git a/Assignments/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/Assignments/MP1_CalebFontenot/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
similarity index 100%
rename from Assignments/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
rename to Assignments/MP1_CalebFontenot/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
diff --git a/Assignments/MP1_CalebFontenot/app/src/main/res/drawable-xhdpi/arrow_left.png b/Assignments/MP1_CalebFontenot/app/src/main/res/drawable-xhdpi/arrow_left.png
new file mode 100644
index 0000000..7a48ab4
Binary files /dev/null and b/Assignments/MP1_CalebFontenot/app/src/main/res/drawable-xhdpi/arrow_left.png differ
diff --git a/Assignments/MP1_CalebFontenot/app/src/main/res/drawable-xhdpi/arrow_right.png b/Assignments/MP1_CalebFontenot/app/src/main/res/drawable-xhdpi/arrow_right.png
new file mode 100644
index 0000000..eb411b8
Binary files /dev/null and b/Assignments/MP1_CalebFontenot/app/src/main/res/drawable-xhdpi/arrow_right.png differ
diff --git a/Assignments/MP1_CalebFontenot/app/src/main/res/drawable-xxhdpi/arrow_left.png b/Assignments/MP1_CalebFontenot/app/src/main/res/drawable-xxhdpi/arrow_left.png
new file mode 100644
index 0000000..76d1e07
Binary files /dev/null and b/Assignments/MP1_CalebFontenot/app/src/main/res/drawable-xxhdpi/arrow_left.png differ
diff --git a/Assignments/MP1_CalebFontenot/app/src/main/res/drawable-xxhdpi/arrow_right.png b/Assignments/MP1_CalebFontenot/app/src/main/res/drawable-xxhdpi/arrow_right.png
new file mode 100644
index 0000000..16115d8
Binary files /dev/null and b/Assignments/MP1_CalebFontenot/app/src/main/res/drawable-xxhdpi/arrow_right.png differ
diff --git a/Assignments/app/src/main/res/drawable/ic_launcher_background.xml b/Assignments/MP1_CalebFontenot/app/src/main/res/drawable/ic_launcher_background.xml
similarity index 100%
rename from Assignments/app/src/main/res/drawable/ic_launcher_background.xml
rename to Assignments/MP1_CalebFontenot/app/src/main/res/drawable/ic_launcher_background.xml
diff --git a/Assignments/MP1_CalebFontenot/app/src/main/res/layout/activity_main.xml b/Assignments/MP1_CalebFontenot/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..440cfa2
--- /dev/null
+++ b/Assignments/MP1_CalebFontenot/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Assignments/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/Assignments/MP1_CalebFontenot/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
similarity index 100%
rename from Assignments/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
rename to Assignments/MP1_CalebFontenot/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
diff --git a/Assignments/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/Assignments/MP1_CalebFontenot/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
similarity index 100%
rename from Assignments/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
rename to Assignments/MP1_CalebFontenot/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
diff --git a/Assignments/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/Assignments/MP1_CalebFontenot/app/src/main/res/mipmap-hdpi/ic_launcher.webp
similarity index 100%
rename from Assignments/app/src/main/res/mipmap-hdpi/ic_launcher.webp
rename to Assignments/MP1_CalebFontenot/app/src/main/res/mipmap-hdpi/ic_launcher.webp
diff --git a/Assignments/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/Assignments/MP1_CalebFontenot/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
similarity index 100%
rename from Assignments/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
rename to Assignments/MP1_CalebFontenot/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
diff --git a/Assignments/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/Assignments/MP1_CalebFontenot/app/src/main/res/mipmap-mdpi/ic_launcher.webp
similarity index 100%
rename from Assignments/app/src/main/res/mipmap-mdpi/ic_launcher.webp
rename to Assignments/MP1_CalebFontenot/app/src/main/res/mipmap-mdpi/ic_launcher.webp
diff --git a/Assignments/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/Assignments/MP1_CalebFontenot/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
similarity index 100%
rename from Assignments/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
rename to Assignments/MP1_CalebFontenot/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
diff --git a/Assignments/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/Assignments/MP1_CalebFontenot/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
similarity index 100%
rename from Assignments/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
rename to Assignments/MP1_CalebFontenot/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
diff --git a/Assignments/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/Assignments/MP1_CalebFontenot/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
similarity index 100%
rename from Assignments/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
rename to Assignments/MP1_CalebFontenot/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
diff --git a/Assignments/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/Assignments/MP1_CalebFontenot/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
similarity index 100%
rename from Assignments/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
rename to Assignments/MP1_CalebFontenot/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
diff --git a/Assignments/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/Assignments/MP1_CalebFontenot/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
similarity index 100%
rename from Assignments/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
rename to Assignments/MP1_CalebFontenot/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
diff --git a/Assignments/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/Assignments/MP1_CalebFontenot/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
similarity index 100%
rename from Assignments/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
rename to Assignments/MP1_CalebFontenot/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
diff --git a/Assignments/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/Assignments/MP1_CalebFontenot/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
similarity index 100%
rename from Assignments/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
rename to Assignments/MP1_CalebFontenot/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
diff --git a/Assignments/app/src/main/res/values-night/themes.xml b/Assignments/MP1_CalebFontenot/app/src/main/res/values-night/themes.xml
similarity index 100%
rename from Assignments/app/src/main/res/values-night/themes.xml
rename to Assignments/MP1_CalebFontenot/app/src/main/res/values-night/themes.xml
diff --git a/Assignments/app/src/main/res/values/colors.xml b/Assignments/MP1_CalebFontenot/app/src/main/res/values/colors.xml
similarity index 100%
rename from Assignments/app/src/main/res/values/colors.xml
rename to Assignments/MP1_CalebFontenot/app/src/main/res/values/colors.xml
diff --git a/Assignments/MP1_CalebFontenot/app/src/main/res/values/strings.xml b/Assignments/MP1_CalebFontenot/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..ce7722b
--- /dev/null
+++ b/Assignments/MP1_CalebFontenot/app/src/main/res/values/strings.xml
@@ -0,0 +1,24 @@
+
+ MP1_CalebFontenot
+ True
+ False
+ Next
+ Previous
+ Correct!
+ Sorry, try again.
+
+ Canberra is the
+capital of Australia.
+ The Pacific Ocean is
+larger than
+the Atlantic Ocean.
+ The Suez Canal
+connects the Red Sea
+and the Indian Ocean.
+ The source of the Nile
+River is in Egypt.
+ The Amazon River is
+the longest river
+in the Americas.
+ Lake Baikal is the world\'s oldest and deepest freshwater lake.
+
\ No newline at end of file
diff --git a/Assignments/app/src/main/res/values/themes.xml b/Assignments/MP1_CalebFontenot/app/src/main/res/values/themes.xml
similarity index 100%
rename from Assignments/app/src/main/res/values/themes.xml
rename to Assignments/MP1_CalebFontenot/app/src/main/res/values/themes.xml
diff --git a/Assignments/app/src/main/res/xml/backup_rules.xml b/Assignments/MP1_CalebFontenot/app/src/main/res/xml/backup_rules.xml
similarity index 100%
rename from Assignments/app/src/main/res/xml/backup_rules.xml
rename to Assignments/MP1_CalebFontenot/app/src/main/res/xml/backup_rules.xml
diff --git a/Assignments/app/src/main/res/xml/data_extraction_rules.xml b/Assignments/MP1_CalebFontenot/app/src/main/res/xml/data_extraction_rules.xml
similarity index 100%
rename from Assignments/app/src/main/res/xml/data_extraction_rules.xml
rename to Assignments/MP1_CalebFontenot/app/src/main/res/xml/data_extraction_rules.xml
diff --git a/Assignments/app/src/test/java/com/calebfontenot/quizchapter2/ExampleUnitTest.java b/Assignments/MP1_CalebFontenot/app/src/test/java/com/calebfontenot/quizchapter2/ExampleUnitTest.java
similarity index 100%
rename from Assignments/app/src/test/java/com/calebfontenot/quizchapter2/ExampleUnitTest.java
rename to Assignments/MP1_CalebFontenot/app/src/test/java/com/calebfontenot/quizchapter2/ExampleUnitTest.java
diff --git a/Assignments/MP1_CalebFontenot/build.gradle b/Assignments/MP1_CalebFontenot/build.gradle
new file mode 100644
index 0000000..ee55c94
--- /dev/null
+++ b/Assignments/MP1_CalebFontenot/build.gradle
@@ -0,0 +1,5 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+ id 'com.android.application' version '8.2.2' apply false
+ id 'com.android.library' version '8.2.2' apply false
+}
\ No newline at end of file
diff --git a/Assignments/gradle.properties b/Assignments/MP1_CalebFontenot/gradle.properties
similarity index 100%
rename from Assignments/gradle.properties
rename to Assignments/MP1_CalebFontenot/gradle.properties
diff --git a/Assignments/gradle/wrapper/gradle-wrapper.jar b/Assignments/MP1_CalebFontenot/gradle/wrapper/gradle-wrapper.jar
similarity index 100%
rename from Assignments/gradle/wrapper/gradle-wrapper.jar
rename to Assignments/MP1_CalebFontenot/gradle/wrapper/gradle-wrapper.jar
diff --git a/Assignments/gradle/wrapper/gradle-wrapper.properties b/Assignments/MP1_CalebFontenot/gradle/wrapper/gradle-wrapper.properties
similarity index 100%
rename from Assignments/gradle/wrapper/gradle-wrapper.properties
rename to Assignments/MP1_CalebFontenot/gradle/wrapper/gradle-wrapper.properties
diff --git a/Assignments/gradlew b/Assignments/MP1_CalebFontenot/gradlew
similarity index 100%
rename from Assignments/gradlew
rename to Assignments/MP1_CalebFontenot/gradlew
diff --git a/Assignments/gradlew.bat b/Assignments/MP1_CalebFontenot/gradlew.bat
similarity index 100%
rename from Assignments/gradlew.bat
rename to Assignments/MP1_CalebFontenot/gradlew.bat
diff --git a/Assignments/MP1_CalebFontenot/settings.gradle b/Assignments/MP1_CalebFontenot/settings.gradle
new file mode 100644
index 0000000..5781980
--- /dev/null
+++ b/Assignments/MP1_CalebFontenot/settings.gradle
@@ -0,0 +1,17 @@
+pluginManagement {
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+rootProject.name = "MP1_CalebFontenot"
+include ':app'
+include ':app'
diff --git a/Assignments/QuizChapter2/.gitignore b/Assignments/QuizChapter2/.gitignore
new file mode 100644
index 0000000..aa724b7
--- /dev/null
+++ b/Assignments/QuizChapter2/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/Assignments/QuizChapter2/.idea/.gitignore b/Assignments/QuizChapter2/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/Assignments/QuizChapter2/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/Assignments/.idea/.name b/Assignments/QuizChapter2/.idea/.name
similarity index 100%
rename from Assignments/.idea/.name
rename to Assignments/QuizChapter2/.idea/.name
diff --git a/Assignments/QuizChapter2/.idea/compiler.xml b/Assignments/QuizChapter2/.idea/compiler.xml
new file mode 100644
index 0000000..b589d56
--- /dev/null
+++ b/Assignments/QuizChapter2/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/.idea/deploymentTargetDropDown.xml b/Assignments/QuizChapter2/.idea/deploymentTargetDropDown.xml
new file mode 100644
index 0000000..0c0c338
--- /dev/null
+++ b/Assignments/QuizChapter2/.idea/deploymentTargetDropDown.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/.idea/discord.xml b/Assignments/QuizChapter2/.idea/discord.xml
new file mode 100644
index 0000000..d8e9561
--- /dev/null
+++ b/Assignments/QuizChapter2/.idea/discord.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/.idea/gradle.xml b/Assignments/QuizChapter2/.idea/gradle.xml
new file mode 100644
index 0000000..b2302f7
--- /dev/null
+++ b/Assignments/QuizChapter2/.idea/gradle.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/.idea/migrations.xml b/Assignments/QuizChapter2/.idea/migrations.xml
new file mode 100644
index 0000000..f8051a6
--- /dev/null
+++ b/Assignments/QuizChapter2/.idea/migrations.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/.idea/misc.xml b/Assignments/QuizChapter2/.idea/misc.xml
new file mode 100644
index 0000000..8978d23
--- /dev/null
+++ b/Assignments/QuizChapter2/.idea/misc.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/Test1/.gitignore b/Assignments/QuizChapter2/Test1/.gitignore
new file mode 100644
index 0000000..aa724b7
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/.gitignore
@@ -0,0 +1,15 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
diff --git a/Assignments/QuizChapter2/Test1/.idea/.gitignore b/Assignments/QuizChapter2/Test1/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/Assignments/QuizChapter2/Test1/.idea/compiler.xml b/Assignments/QuizChapter2/Test1/.idea/compiler.xml
new file mode 100644
index 0000000..b589d56
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/Test1/.idea/deploymentTargetDropDown.xml b/Assignments/QuizChapter2/Test1/.idea/deploymentTargetDropDown.xml
new file mode 100644
index 0000000..7e4bc03
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/.idea/deploymentTargetDropDown.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/Test1/.idea/gradle.xml b/Assignments/QuizChapter2/Test1/.idea/gradle.xml
new file mode 100644
index 0000000..ad32906
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/.idea/gradle.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/Test1/.idea/misc.xml b/Assignments/QuizChapter2/Test1/.idea/misc.xml
new file mode 100644
index 0000000..9f71c83
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/.idea/misc.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/Test1/app/.gitignore b/Assignments/QuizChapter2/Test1/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/Test1/app/build.gradle b/Assignments/QuizChapter2/Test1/app/build.gradle
new file mode 100644
index 0000000..976920d
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/app/build.gradle
@@ -0,0 +1,39 @@
+plugins {
+ id 'com.android.application'
+}
+
+android {
+ namespace 'com.calebfontenot.test1'
+ compileSdk 34
+
+ defaultConfig {
+ applicationId "com.calebfontenot.test1"
+ minSdk 30
+ targetSdk 34
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+}
+
+dependencies {
+
+ implementation 'androidx.appcompat:appcompat:1.6.1'
+ implementation 'com.google.android.material:material:1.11.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
+ testImplementation 'junit:junit:4.13.2'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.5'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
+}
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/Test1/app/proguard-rules.pro b/Assignments/QuizChapter2/Test1/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/Test1/app/src/androidTest/java/com/calebfontenot/test1/ExampleInstrumentedTest.java b/Assignments/QuizChapter2/Test1/app/src/androidTest/java/com/calebfontenot/test1/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..22e6aae
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/app/src/androidTest/java/com/calebfontenot/test1/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.calebfontenot.test1;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ assertEquals("com.calebfontenot.test1", appContext.getPackageName());
+ }
+}
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/Test1/app/src/main/AndroidManifest.xml b/Assignments/QuizChapter2/Test1/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..22d1340
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/app/src/main/AndroidManifest.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/Test1/app/src/main/java/com/calebfontenot/test1/HelloWorld.java b/Assignments/QuizChapter2/Test1/app/src/main/java/com/calebfontenot/test1/HelloWorld.java
new file mode 100644
index 0000000..4632b9a
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/app/src/main/java/com/calebfontenot/test1/HelloWorld.java
@@ -0,0 +1,36 @@
+package com.calebfontenot.test1;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.os.Bundle;
+import android.service.quickaccesswallet.QuickAccessWalletService;
+import android.view.View;
+import android.widget.Button;
+import android.widget.Toast;
+
+public class HelloWorld extends AppCompatActivity {
+
+ private Button trueButton;
+ private Button falseButton;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_hello);
+ trueButton = this.findViewById(R.id.true_button);
+ falseButton = this.findViewById(R.id.false_button);
+
+ trueButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Toast.makeText(HelloWorld.this, "You are correct!", Toast.LENGTH_SHORT).show();
+ }
+ });
+ falseButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Toast.makeText(HelloWorld.this, "You are incorrect!", Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+}
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/Test1/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/Assignments/QuizChapter2/Test1/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/Test1/app/src/main/res/drawable/ic_launcher_background.xml b/Assignments/QuizChapter2/Test1/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..07d5da9
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Assignments/QuizChapter2/Test1/app/src/main/res/layout/activity_hello.xml b/Assignments/QuizChapter2/Test1/app/src/main/res/layout/activity_hello.xml
new file mode 100644
index 0000000..9f6691c
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/app/src/main/res/layout/activity_hello.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..6f3b755
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..6f3b755
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000..c209e78
Binary files /dev/null and b/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..b2dfe3d
Binary files /dev/null and b/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000..4f0f1d6
Binary files /dev/null and b/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..62b611d
Binary files /dev/null and b/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000..948a307
Binary files /dev/null and b/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..1b9a695
Binary files /dev/null and b/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..28d4b77
Binary files /dev/null and b/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9287f50
Binary files /dev/null and b/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..aa7d642
Binary files /dev/null and b/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9126ae3
Binary files /dev/null and b/Assignments/QuizChapter2/Test1/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/Assignments/QuizChapter2/Test1/app/src/main/res/values-night/themes.xml b/Assignments/QuizChapter2/Test1/app/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000..0eaa543
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/Test1/app/src/main/res/values/colors.xml b/Assignments/QuizChapter2/Test1/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..5d728eb
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/app/src/main/res/values/colors.xml
@@ -0,0 +1,11 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+ #FFFF0000
+
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/Test1/app/src/main/res/values/strings.xml b/Assignments/QuizChapter2/Test1/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..8785d84
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/app/src/main/res/values/strings.xml
@@ -0,0 +1,8 @@
+
+ Test1 😮
+
+ Canberra is the capital of Australia.
+ True
+ False
+
+
diff --git a/Assignments/QuizChapter2/Test1/app/src/main/res/values/themes.xml b/Assignments/QuizChapter2/Test1/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..04d06f8
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/app/src/main/res/values/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/Test1/app/src/main/res/xml/backup_rules.xml b/Assignments/QuizChapter2/Test1/app/src/main/res/xml/backup_rules.xml
new file mode 100644
index 0000000..fa0f996
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/app/src/main/res/xml/backup_rules.xml
@@ -0,0 +1,13 @@
+
+
+
+
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/Test1/app/src/main/res/xml/data_extraction_rules.xml b/Assignments/QuizChapter2/Test1/app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 0000000..9ee9997
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/Test1/app/src/test/java/com/calebfontenot/test1/ExampleUnitTest.java b/Assignments/QuizChapter2/Test1/app/src/test/java/com/calebfontenot/test1/ExampleUnitTest.java
new file mode 100644
index 0000000..2580a77
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/app/src/test/java/com/calebfontenot/test1/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.calebfontenot.test1;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/Test1/build.gradle b/Assignments/QuizChapter2/Test1/build.gradle
new file mode 100644
index 0000000..57d2e3b
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/build.gradle
@@ -0,0 +1,5 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+ id 'com.android.application' version '8.0.0-alpha11' apply false
+ id 'com.android.library' version '8.0.0-alpha11' apply false
+}
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/Test1/gradle.properties b/Assignments/QuizChapter2/Test1/gradle.properties
new file mode 100644
index 0000000..3e927b1
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/gradle.properties
@@ -0,0 +1,21 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/Test1/gradle/wrapper/gradle-wrapper.jar b/Assignments/QuizChapter2/Test1/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..e708b1c
Binary files /dev/null and b/Assignments/QuizChapter2/Test1/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Assignments/QuizChapter2/Test1/gradle/wrapper/gradle-wrapper.properties b/Assignments/QuizChapter2/Test1/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..9a3c763
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Thu Jan 11 13:40:41 CST 2024
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/Assignments/QuizChapter2/Test1/gradlew b/Assignments/QuizChapter2/Test1/gradlew
new file mode 100755
index 0000000..4f906e0
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/Assignments/QuizChapter2/Test1/gradlew.bat b/Assignments/QuizChapter2/Test1/gradlew.bat
new file mode 100644
index 0000000..ac1b06f
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Assignments/QuizChapter2/Test1/settings.gradle b/Assignments/QuizChapter2/Test1/settings.gradle
new file mode 100644
index 0000000..43f56f0
--- /dev/null
+++ b/Assignments/QuizChapter2/Test1/settings.gradle
@@ -0,0 +1,16 @@
+pluginManagement {
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+dependencyResolutionManagement {
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+rootProject.name = "Test1"
+include ':app'
diff --git a/Assignments/QuizChapter2/app/.gitignore b/Assignments/QuizChapter2/app/.gitignore
new file mode 100644
index 0000000..42afabf
--- /dev/null
+++ b/Assignments/QuizChapter2/app/.gitignore
@@ -0,0 +1 @@
+/build
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/app/build.gradle b/Assignments/QuizChapter2/app/build.gradle
new file mode 100644
index 0000000..5a2da95
--- /dev/null
+++ b/Assignments/QuizChapter2/app/build.gradle
@@ -0,0 +1,39 @@
+plugins {
+ id 'com.android.application'
+}
+
+android {
+ namespace 'com.calebfontenot.quizchapter2'
+ compileSdk 34
+
+ defaultConfig {
+ applicationId "com.calebfontenot.quizchapter2"
+ minSdk 30
+ targetSdk 34
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+}
+
+dependencies {
+
+ implementation 'androidx.appcompat:appcompat:1.6.1'
+ implementation 'com.google.android.material:material:1.11.0'
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
+ testImplementation 'junit:junit:4.13.2'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.5'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
+}
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/app/proguard-rules.pro b/Assignments/QuizChapter2/app/proguard-rules.pro
new file mode 100644
index 0000000..481bb43
--- /dev/null
+++ b/Assignments/QuizChapter2/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/app/src/androidTest/java/com/calebfontenot/quizchapter2/ExampleInstrumentedTest.java b/Assignments/QuizChapter2/app/src/androidTest/java/com/calebfontenot/quizchapter2/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..cf0ee54
--- /dev/null
+++ b/Assignments/QuizChapter2/app/src/androidTest/java/com/calebfontenot/quizchapter2/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.calebfontenot.quizchapter2;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ assertEquals("com.calebfontenot.quizchapter2", appContext.getPackageName());
+ }
+}
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/app/src/main/AndroidManifest.xml b/Assignments/QuizChapter2/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..c239a24
--- /dev/null
+++ b/Assignments/QuizChapter2/app/src/main/AndroidManifest.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Assignments/app/src/main/java/com/calebfontenot/quizchapter2/MainActivity.java b/Assignments/QuizChapter2/app/src/main/java/com/calebfontenot/quizchapter2/MainActivity.java
similarity index 100%
rename from Assignments/app/src/main/java/com/calebfontenot/quizchapter2/MainActivity.java
rename to Assignments/QuizChapter2/app/src/main/java/com/calebfontenot/quizchapter2/MainActivity.java
diff --git a/Assignments/QuizChapter2/app/src/main/java/com/calebfontenot/quizchapter2/Question.java b/Assignments/QuizChapter2/app/src/main/java/com/calebfontenot/quizchapter2/Question.java
new file mode 100644
index 0000000..61ad12b
--- /dev/null
+++ b/Assignments/QuizChapter2/app/src/main/java/com/calebfontenot/quizchapter2/Question.java
@@ -0,0 +1,28 @@
+package com.calebfontenot.quizchapter2;
+
+public class Question {
+ private int mTextResId;
+ private boolean mAnswerTrue;
+
+ public int getmTextResId() {
+ return mTextResId;
+ }
+
+ public void setmTextResId(int mTextResId) {
+ this.mTextResId = mTextResId;
+ }
+
+ public boolean ismAnswerTrue() {
+ return mAnswerTrue;
+ }
+
+ public void setmAnswerTrue(boolean mAnswerTrue) {
+ this.mAnswerTrue = mAnswerTrue;
+ }
+
+ public Question(int textResId, boolean answerTrue) {
+ mTextResId = textResId;
+ mAnswerTrue = answerTrue;
+
+ }
+}
diff --git a/Assignments/QuizChapter2/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/Assignments/QuizChapter2/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..2b068d1
--- /dev/null
+++ b/Assignments/QuizChapter2/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/app/src/main/res/drawable/ic_launcher_background.xml b/Assignments/QuizChapter2/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..07d5da9
--- /dev/null
+++ b/Assignments/QuizChapter2/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Assignments/app/src/main/res/layout/activity_main.xml b/Assignments/QuizChapter2/app/src/main/res/layout/activity_main.xml
similarity index 100%
rename from Assignments/app/src/main/res/layout/activity_main.xml
rename to Assignments/QuizChapter2/app/src/main/res/layout/activity_main.xml
diff --git a/Assignments/QuizChapter2/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/Assignments/QuizChapter2/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..6f3b755
--- /dev/null
+++ b/Assignments/QuizChapter2/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/Assignments/QuizChapter2/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..6f3b755
--- /dev/null
+++ b/Assignments/QuizChapter2/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/Assignments/QuizChapter2/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000..c209e78
Binary files /dev/null and b/Assignments/QuizChapter2/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/Assignments/QuizChapter2/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/Assignments/QuizChapter2/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..b2dfe3d
Binary files /dev/null and b/Assignments/QuizChapter2/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/Assignments/QuizChapter2/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/Assignments/QuizChapter2/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000..4f0f1d6
Binary files /dev/null and b/Assignments/QuizChapter2/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/Assignments/QuizChapter2/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/Assignments/QuizChapter2/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..62b611d
Binary files /dev/null and b/Assignments/QuizChapter2/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/Assignments/QuizChapter2/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/Assignments/QuizChapter2/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000..948a307
Binary files /dev/null and b/Assignments/QuizChapter2/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/Assignments/QuizChapter2/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/Assignments/QuizChapter2/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..1b9a695
Binary files /dev/null and b/Assignments/QuizChapter2/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/Assignments/QuizChapter2/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/Assignments/QuizChapter2/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..28d4b77
Binary files /dev/null and b/Assignments/QuizChapter2/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/Assignments/QuizChapter2/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/Assignments/QuizChapter2/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9287f50
Binary files /dev/null and b/Assignments/QuizChapter2/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/Assignments/QuizChapter2/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/Assignments/QuizChapter2/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..aa7d642
Binary files /dev/null and b/Assignments/QuizChapter2/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/Assignments/QuizChapter2/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/Assignments/QuizChapter2/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..9126ae3
Binary files /dev/null and b/Assignments/QuizChapter2/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/Assignments/QuizChapter2/app/src/main/res/values-night/themes.xml b/Assignments/QuizChapter2/app/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000..f285c9f
--- /dev/null
+++ b/Assignments/QuizChapter2/app/src/main/res/values-night/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/app/src/main/res/values/colors.xml b/Assignments/QuizChapter2/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..f8c6127
--- /dev/null
+++ b/Assignments/QuizChapter2/app/src/main/res/values/colors.xml
@@ -0,0 +1,10 @@
+
+
+ #FFBB86FC
+ #FF6200EE
+ #FF3700B3
+ #FF03DAC5
+ #FF018786
+ #FF000000
+ #FFFFFFFF
+
\ No newline at end of file
diff --git a/Assignments/app/src/main/res/values/strings.xml b/Assignments/QuizChapter2/app/src/main/res/values/strings.xml
similarity index 100%
rename from Assignments/app/src/main/res/values/strings.xml
rename to Assignments/QuizChapter2/app/src/main/res/values/strings.xml
diff --git a/Assignments/QuizChapter2/app/src/main/res/values/themes.xml b/Assignments/QuizChapter2/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..69d8674
--- /dev/null
+++ b/Assignments/QuizChapter2/app/src/main/res/values/themes.xml
@@ -0,0 +1,16 @@
+
+
+
+
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/app/src/main/res/xml/backup_rules.xml b/Assignments/QuizChapter2/app/src/main/res/xml/backup_rules.xml
new file mode 100644
index 0000000..fa0f996
--- /dev/null
+++ b/Assignments/QuizChapter2/app/src/main/res/xml/backup_rules.xml
@@ -0,0 +1,13 @@
+
+
+
+
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/app/src/main/res/xml/data_extraction_rules.xml b/Assignments/QuizChapter2/app/src/main/res/xml/data_extraction_rules.xml
new file mode 100644
index 0000000..9ee9997
--- /dev/null
+++ b/Assignments/QuizChapter2/app/src/main/res/xml/data_extraction_rules.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/app/src/test/java/com/calebfontenot/quizchapter2/ExampleUnitTest.java b/Assignments/QuizChapter2/app/src/test/java/com/calebfontenot/quizchapter2/ExampleUnitTest.java
new file mode 100644
index 0000000..d02c9d4
--- /dev/null
+++ b/Assignments/QuizChapter2/app/src/test/java/com/calebfontenot/quizchapter2/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.calebfontenot.quizchapter2;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Assignments/build.gradle b/Assignments/QuizChapter2/build.gradle
similarity index 100%
rename from Assignments/build.gradle
rename to Assignments/QuizChapter2/build.gradle
diff --git a/Assignments/QuizChapter2/gradle.properties b/Assignments/QuizChapter2/gradle.properties
new file mode 100644
index 0000000..f511a31
--- /dev/null
+++ b/Assignments/QuizChapter2/gradle.properties
@@ -0,0 +1,22 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Enables namespacing of each library's R class so that its R class includes only the
+# resources declared in the library itself and none from the library's dependencies,
+# thereby reducing the size of the R class for that library
+android.nonTransitiveRClass=true
+android.nonFinalResIds=false
\ No newline at end of file
diff --git a/Assignments/QuizChapter2/gradle/wrapper/gradle-wrapper.jar b/Assignments/QuizChapter2/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..e708b1c
Binary files /dev/null and b/Assignments/QuizChapter2/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Assignments/QuizChapter2/gradle/wrapper/gradle-wrapper.properties b/Assignments/QuizChapter2/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..b1df686
--- /dev/null
+++ b/Assignments/QuizChapter2/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Jan 19 11:12:34 CST 2024
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/Assignments/QuizChapter2/gradlew b/Assignments/QuizChapter2/gradlew
new file mode 100755
index 0000000..4f906e0
--- /dev/null
+++ b/Assignments/QuizChapter2/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/Assignments/QuizChapter2/gradlew.bat b/Assignments/QuizChapter2/gradlew.bat
new file mode 100644
index 0000000..ac1b06f
--- /dev/null
+++ b/Assignments/QuizChapter2/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Assignments/settings.gradle b/Assignments/QuizChapter2/settings.gradle
similarity index 100%
rename from Assignments/settings.gradle
rename to Assignments/QuizChapter2/settings.gradle
diff --git a/Book Files/01_FirstApp/.DS_Store b/Book Files/01_FirstApp/.DS_Store
new file mode 100644
index 0000000..5008ddf
Binary files /dev/null and b/Book Files/01_FirstApp/.DS_Store differ
diff --git a/Book Files/01_FirstApp/GeoQuiz/.gitignore b/Book Files/01_FirstApp/GeoQuiz/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/Book Files/01_FirstApp/GeoQuiz/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Book Files/01_FirstApp/GeoQuiz/.idea/compiler.xml b/Book Files/01_FirstApp/GeoQuiz/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/Book Files/01_FirstApp/GeoQuiz/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/01_FirstApp/GeoQuiz/.idea/copyright/profiles_settings.xml b/Book Files/01_FirstApp/GeoQuiz/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/Book Files/01_FirstApp/GeoQuiz/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Book Files/01_FirstApp/GeoQuiz/.idea/encodings.xml b/Book Files/01_FirstApp/GeoQuiz/.idea/encodings.xml
new file mode 100644
index 0000000..97626ba
--- /dev/null
+++ b/Book Files/01_FirstApp/GeoQuiz/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/01_FirstApp/GeoQuiz/.idea/gradle.xml b/Book Files/01_FirstApp/GeoQuiz/.idea/gradle.xml
new file mode 100644
index 0000000..0e23f8e
--- /dev/null
+++ b/Book Files/01_FirstApp/GeoQuiz/.idea/gradle.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/01_FirstApp/GeoQuiz/.idea/misc.xml b/Book Files/01_FirstApp/GeoQuiz/.idea/misc.xml
new file mode 100644
index 0000000..fbb6828
--- /dev/null
+++ b/Book Files/01_FirstApp/GeoQuiz/.idea/misc.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/01_FirstApp/GeoQuiz/.idea/modules.xml b/Book Files/01_FirstApp/GeoQuiz/.idea/modules.xml
new file mode 100644
index 0000000..53041ba
--- /dev/null
+++ b/Book Files/01_FirstApp/GeoQuiz/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/01_FirstApp/GeoQuiz/.idea/runConfigurations.xml b/Book Files/01_FirstApp/GeoQuiz/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/Book Files/01_FirstApp/GeoQuiz/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/01_FirstApp/GeoQuiz/app/.gitignore b/Book Files/01_FirstApp/GeoQuiz/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/01_FirstApp/GeoQuiz/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/01_FirstApp/GeoQuiz/app/build.gradle b/Book Files/01_FirstApp/GeoQuiz/app/build.gradle
new file mode 100644
index 0000000..a0284ac
--- /dev/null
+++ b/Book Files/01_FirstApp/GeoQuiz/app/build.gradle
@@ -0,0 +1,29 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ applicationId "com.bignerdranch.android.geoquiz"
+ minSdkVersion 19
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ testCompile 'junit:junit:4.12'
+}
diff --git a/Book Files/01_FirstApp/GeoQuiz/app/proguard-rules.pro b/Book Files/01_FirstApp/GeoQuiz/app/proguard-rules.pro
new file mode 100644
index 0000000..99da964
--- /dev/null
+++ b/Book Files/01_FirstApp/GeoQuiz/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/chris/AndroidDeveloper/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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 *;
+#}
diff --git a/Book Files/01_FirstApp/GeoQuiz/app/src/androidTest/java/com/bignerdranch/android/geoquiz/ExampleInstrumentedTest.java b/Book Files/01_FirstApp/GeoQuiz/app/src/androidTest/java/com/bignerdranch/android/geoquiz/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..68985dc
--- /dev/null
+++ b/Book Files/01_FirstApp/GeoQuiz/app/src/androidTest/java/com/bignerdranch/android/geoquiz/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.geoquiz;
+
+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.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.bignerdranch.android.geoquiz", appContext.getPackageName());
+ }
+}
diff --git a/Book Files/01_FirstApp/GeoQuiz/app/src/main/AndroidManifest.xml b/Book Files/01_FirstApp/GeoQuiz/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..acb8d7a
--- /dev/null
+++ b/Book Files/01_FirstApp/GeoQuiz/app/src/main/AndroidManifest.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/01_FirstApp/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/QuizActivity.java b/Book Files/01_FirstApp/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/QuizActivity.java
new file mode 100644
index 0000000..3edd9eb
--- /dev/null
+++ b/Book Files/01_FirstApp/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/QuizActivity.java
@@ -0,0 +1,39 @@
+package com.bignerdranch.android.geoquiz;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+import android.widget.Button;
+import android.widget.Toast;
+
+public class QuizActivity extends AppCompatActivity {
+
+ private Button mTrueButton;
+ private Button mFalseButton;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_quiz);
+
+ mTrueButton = (Button) findViewById(R.id.true_button);
+ mTrueButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Toast.makeText(QuizActivity.this,
+ R.string.correct_toast,
+ Toast.LENGTH_SHORT).show();
+ }
+ });
+
+ mFalseButton = (Button) findViewById(R.id.false_button);
+ mFalseButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Toast.makeText(QuizActivity.this,
+ R.string.incorrect_toast,
+ Toast.LENGTH_SHORT).show();
+ }
+ });
+ }
+}
diff --git a/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/layout/activity_quiz.xml b/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/layout/activity_quiz.xml
new file mode 100644
index 0000000..f6bdb19
--- /dev/null
+++ b/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/layout/activity_quiz.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/values-w820dp/dimens.xml b/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/values/colors.xml b/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/values/dimens.xml b/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/values/strings.xml b/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..7d5bb5d
--- /dev/null
+++ b/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/values/strings.xml
@@ -0,0 +1,8 @@
+
+ GeoQuiz
+ Canberra is the capital of Australia.
+ True
+ False
+ Correct!
+ Incorrect!
+
diff --git a/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/values/styles.xml b/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/Book Files/01_FirstApp/GeoQuiz/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Book Files/01_FirstApp/GeoQuiz/app/src/test/java/com/bignerdranch/android/geoquiz/ExampleUnitTest.java b/Book Files/01_FirstApp/GeoQuiz/app/src/test/java/com/bignerdranch/android/geoquiz/ExampleUnitTest.java
new file mode 100644
index 0000000..6b78649
--- /dev/null
+++ b/Book Files/01_FirstApp/GeoQuiz/app/src/test/java/com/bignerdranch/android/geoquiz/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.geoquiz;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/01_FirstApp/GeoQuiz/build.gradle b/Book Files/01_FirstApp/GeoQuiz/build.gradle
new file mode 100644
index 0000000..1ea4bd0
--- /dev/null
+++ b/Book Files/01_FirstApp/GeoQuiz/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Book Files/01_FirstApp/GeoQuiz/gradle.properties b/Book Files/01_FirstApp/GeoQuiz/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/Book Files/01_FirstApp/GeoQuiz/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/Book Files/01_FirstApp/GeoQuiz/gradle/wrapper/gradle-wrapper.jar b/Book Files/01_FirstApp/GeoQuiz/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/Book Files/01_FirstApp/GeoQuiz/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/01_FirstApp/GeoQuiz/gradle/wrapper/gradle-wrapper.properties b/Book Files/01_FirstApp/GeoQuiz/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a82392d
--- /dev/null
+++ b/Book Files/01_FirstApp/GeoQuiz/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/01_FirstApp/GeoQuiz/gradlew b/Book Files/01_FirstApp/GeoQuiz/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/Book Files/01_FirstApp/GeoQuiz/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/01_FirstApp/GeoQuiz/gradlew.bat b/Book Files/01_FirstApp/GeoQuiz/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/01_FirstApp/GeoQuiz/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/01_FirstApp/GeoQuiz/settings.gradle b/Book Files/01_FirstApp/GeoQuiz/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/01_FirstApp/GeoQuiz/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/01_FirstApp/LICENSE.txt b/Book Files/01_FirstApp/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Book Files/01_FirstApp/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Book Files/02_MVC/.DS_Store b/Book Files/02_MVC/.DS_Store
new file mode 100644
index 0000000..5008ddf
Binary files /dev/null and b/Book Files/02_MVC/.DS_Store differ
diff --git a/Book Files/02_MVC/GeoQuiz/.gitignore b/Book Files/02_MVC/GeoQuiz/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/Book Files/02_MVC/GeoQuiz/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Book Files/02_MVC/GeoQuiz/.idea/compiler.xml b/Book Files/02_MVC/GeoQuiz/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/Book Files/02_MVC/GeoQuiz/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/02_MVC/GeoQuiz/.idea/copyright/profiles_settings.xml b/Book Files/02_MVC/GeoQuiz/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/Book Files/02_MVC/GeoQuiz/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Book Files/02_MVC/GeoQuiz/.idea/encodings.xml b/Book Files/02_MVC/GeoQuiz/.idea/encodings.xml
new file mode 100644
index 0000000..97626ba
--- /dev/null
+++ b/Book Files/02_MVC/GeoQuiz/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/02_MVC/GeoQuiz/.idea/gradle.xml b/Book Files/02_MVC/GeoQuiz/.idea/gradle.xml
new file mode 100644
index 0000000..0e23f8e
--- /dev/null
+++ b/Book Files/02_MVC/GeoQuiz/.idea/gradle.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/02_MVC/GeoQuiz/.idea/misc.xml b/Book Files/02_MVC/GeoQuiz/.idea/misc.xml
new file mode 100644
index 0000000..1d9d626
--- /dev/null
+++ b/Book Files/02_MVC/GeoQuiz/.idea/misc.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/02_MVC/GeoQuiz/.idea/modules.xml b/Book Files/02_MVC/GeoQuiz/.idea/modules.xml
new file mode 100644
index 0000000..53041ba
--- /dev/null
+++ b/Book Files/02_MVC/GeoQuiz/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/02_MVC/GeoQuiz/.idea/runConfigurations.xml b/Book Files/02_MVC/GeoQuiz/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/Book Files/02_MVC/GeoQuiz/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/02_MVC/GeoQuiz/app/.gitignore b/Book Files/02_MVC/GeoQuiz/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/02_MVC/GeoQuiz/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/02_MVC/GeoQuiz/app/build.gradle b/Book Files/02_MVC/GeoQuiz/app/build.gradle
new file mode 100644
index 0000000..a0284ac
--- /dev/null
+++ b/Book Files/02_MVC/GeoQuiz/app/build.gradle
@@ -0,0 +1,29 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ applicationId "com.bignerdranch.android.geoquiz"
+ minSdkVersion 19
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ testCompile 'junit:junit:4.12'
+}
diff --git a/Book Files/02_MVC/GeoQuiz/app/proguard-rules.pro b/Book Files/02_MVC/GeoQuiz/app/proguard-rules.pro
new file mode 100644
index 0000000..99da964
--- /dev/null
+++ b/Book Files/02_MVC/GeoQuiz/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/chris/AndroidDeveloper/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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 *;
+#}
diff --git a/Book Files/02_MVC/GeoQuiz/app/src/androidTest/java/com/bignerdranch/android/geoquiz/ExampleInstrumentedTest.java b/Book Files/02_MVC/GeoQuiz/app/src/androidTest/java/com/bignerdranch/android/geoquiz/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..68985dc
--- /dev/null
+++ b/Book Files/02_MVC/GeoQuiz/app/src/androidTest/java/com/bignerdranch/android/geoquiz/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.geoquiz;
+
+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.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.bignerdranch.android.geoquiz", appContext.getPackageName());
+ }
+}
diff --git a/Book Files/02_MVC/GeoQuiz/app/src/main/AndroidManifest.xml b/Book Files/02_MVC/GeoQuiz/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..acb8d7a
--- /dev/null
+++ b/Book Files/02_MVC/GeoQuiz/app/src/main/AndroidManifest.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/02_MVC/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/Question.java b/Book Files/02_MVC/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/Question.java
new file mode 100644
index 0000000..b879043
--- /dev/null
+++ b/Book Files/02_MVC/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/Question.java
@@ -0,0 +1,28 @@
+package com.bignerdranch.android.geoquiz;
+
+public class Question {
+
+ private int mTextResId;
+ private boolean mAnswerTrue;
+
+ public Question(int textResId, boolean answerTrue) {
+ mTextResId = textResId;
+ mAnswerTrue = answerTrue;
+ }
+
+ public int getTextResId() {
+ return mTextResId;
+ }
+
+ public void setTextResId(int textResId) {
+ mTextResId = textResId;
+ }
+
+ public boolean isAnswerTrue() {
+ return mAnswerTrue;
+ }
+
+ public void setAnswerTrue(boolean answerTrue) {
+ mAnswerTrue = answerTrue;
+ }
+}
diff --git a/Book Files/02_MVC/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/QuizActivity.java b/Book Files/02_MVC/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/QuizActivity.java
new file mode 100644
index 0000000..af6db1e
--- /dev/null
+++ b/Book Files/02_MVC/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/QuizActivity.java
@@ -0,0 +1,82 @@
+package com.bignerdranch.android.geoquiz;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class QuizActivity extends AppCompatActivity {
+
+ private Button mTrueButton;
+ private Button mFalseButton;
+ private Button mNextButton;
+ private TextView mQuestionTextView;
+
+ private Question[] mQuestionBank = new Question[] {
+ new Question(R.string.question_australia, true),
+ new Question(R.string.question_oceans, true),
+ new Question(R.string.question_mideast, false),
+ new Question(R.string.question_africa, false),
+ new Question(R.string.question_americas, true),
+ new Question(R.string.question_asia, true),
+ };
+
+ private int mCurrentIndex = 0;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_quiz);
+
+ mQuestionTextView = (TextView) findViewById(R.id.question_text_view);
+
+ mTrueButton = (Button) findViewById(R.id.true_button);
+ mTrueButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ checkAnswer(true);
+ }
+ });
+
+ mFalseButton = (Button) findViewById(R.id.false_button);
+ mFalseButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ checkAnswer(false);
+ }
+ });
+
+ mNextButton = (Button) findViewById(R.id.next_button);
+ mNextButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCurrentIndex = (mCurrentIndex + 1) % mQuestionBank.length;
+ updateQuestion();
+ }
+ });
+
+ updateQuestion();
+ }
+
+ private void updateQuestion() {
+ int question = mQuestionBank[mCurrentIndex].getTextResId();
+ mQuestionTextView.setText(question);
+ }
+
+ private void checkAnswer(boolean userPressedTrue) {
+ boolean answerIsTrue = mQuestionBank[mCurrentIndex].isAnswerTrue();
+
+ int messageResId = 0;
+
+ if (userPressedTrue == answerIsTrue) {
+ messageResId = R.string.correct_toast;
+ } else {
+ messageResId = R.string.incorrect_toast;
+ }
+
+ Toast.makeText(this, messageResId, Toast.LENGTH_SHORT)
+ .show();
+ }
+}
diff --git a/Book Files/02_MVC/GeoQuiz/app/src/main/res/drawable-hdpi/arrow_left.png b/Book Files/02_MVC/GeoQuiz/app/src/main/res/drawable-hdpi/arrow_left.png
new file mode 100644
index 0000000..60186ca
Binary files /dev/null and b/Book Files/02_MVC/GeoQuiz/app/src/main/res/drawable-hdpi/arrow_left.png differ
diff --git a/Book Files/02_MVC/GeoQuiz/app/src/main/res/drawable-hdpi/arrow_right.png b/Book Files/02_MVC/GeoQuiz/app/src/main/res/drawable-hdpi/arrow_right.png
new file mode 100644
index 0000000..5eb216a
Binary files /dev/null and b/Book Files/02_MVC/GeoQuiz/app/src/main/res/drawable-hdpi/arrow_right.png differ
diff --git a/Book Files/02_MVC/GeoQuiz/app/src/main/res/drawable-mdpi/arrow_left.png b/Book Files/02_MVC/GeoQuiz/app/src/main/res/drawable-mdpi/arrow_left.png
new file mode 100644
index 0000000..71c1a42
Binary files /dev/null and b/Book Files/02_MVC/GeoQuiz/app/src/main/res/drawable-mdpi/arrow_left.png differ
diff --git a/Book Files/02_MVC/GeoQuiz/app/src/main/res/drawable-mdpi/arrow_right.png b/Book Files/02_MVC/GeoQuiz/app/src/main/res/drawable-mdpi/arrow_right.png
new file mode 100644
index 0000000..c505b45
Binary files /dev/null and b/Book Files/02_MVC/GeoQuiz/app/src/main/res/drawable-mdpi/arrow_right.png differ
diff --git a/Book Files/02_MVC/GeoQuiz/app/src/main/res/drawable-xhdpi/arrow_left.png b/Book Files/02_MVC/GeoQuiz/app/src/main/res/drawable-xhdpi/arrow_left.png
new file mode 100644
index 0000000..7a48ab4
Binary files /dev/null and b/Book Files/02_MVC/GeoQuiz/app/src/main/res/drawable-xhdpi/arrow_left.png differ
diff --git a/Book Files/02_MVC/GeoQuiz/app/src/main/res/drawable-xhdpi/arrow_right.png b/Book Files/02_MVC/GeoQuiz/app/src/main/res/drawable-xhdpi/arrow_right.png
new file mode 100644
index 0000000..eb411b8
Binary files /dev/null and b/Book Files/02_MVC/GeoQuiz/app/src/main/res/drawable-xhdpi/arrow_right.png differ
diff --git a/Book Files/02_MVC/GeoQuiz/app/src/main/res/drawable-xxhdpi/arrow_left.png b/Book Files/02_MVC/GeoQuiz/app/src/main/res/drawable-xxhdpi/arrow_left.png
new file mode 100644
index 0000000..76d1e07
Binary files /dev/null and b/Book Files/02_MVC/GeoQuiz/app/src/main/res/drawable-xxhdpi/arrow_left.png differ
diff --git a/Book Files/02_MVC/GeoQuiz/app/src/main/res/drawable-xxhdpi/arrow_right.png b/Book Files/02_MVC/GeoQuiz/app/src/main/res/drawable-xxhdpi/arrow_right.png
new file mode 100644
index 0000000..16115d8
Binary files /dev/null and b/Book Files/02_MVC/GeoQuiz/app/src/main/res/drawable-xxhdpi/arrow_right.png differ
diff --git a/Book Files/02_MVC/GeoQuiz/app/src/main/res/layout/activity_quiz.xml b/Book Files/02_MVC/GeoQuiz/app/src/main/res/layout/activity_quiz.xml
new file mode 100644
index 0000000..27aed5f
--- /dev/null
+++ b/Book Files/02_MVC/GeoQuiz/app/src/main/res/layout/activity_quiz.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Book Files/02_MVC/GeoQuiz/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Book Files/02_MVC/GeoQuiz/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/Book Files/02_MVC/GeoQuiz/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Book Files/02_MVC/GeoQuiz/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Book Files/02_MVC/GeoQuiz/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/Book Files/02_MVC/GeoQuiz/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Book Files/02_MVC/GeoQuiz/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Book Files/02_MVC/GeoQuiz/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/Book Files/02_MVC/GeoQuiz/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Book Files/02_MVC/GeoQuiz/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Book Files/02_MVC/GeoQuiz/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/Book Files/02_MVC/GeoQuiz/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Book Files/02_MVC/GeoQuiz/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Book Files/02_MVC/GeoQuiz/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/Book Files/02_MVC/GeoQuiz/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Book Files/02_MVC/GeoQuiz/app/src/main/res/values-w820dp/dimens.xml b/Book Files/02_MVC/GeoQuiz/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/Book Files/02_MVC/GeoQuiz/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Book Files/02_MVC/GeoQuiz/app/src/main/res/values/colors.xml b/Book Files/02_MVC/GeoQuiz/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/Book Files/02_MVC/GeoQuiz/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/Book Files/02_MVC/GeoQuiz/app/src/main/res/values/dimens.xml b/Book Files/02_MVC/GeoQuiz/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/Book Files/02_MVC/GeoQuiz/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/Book Files/02_MVC/GeoQuiz/app/src/main/res/values/strings.xml b/Book Files/02_MVC/GeoQuiz/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..eac26c3
--- /dev/null
+++ b/Book Files/02_MVC/GeoQuiz/app/src/main/res/values/strings.xml
@@ -0,0 +1,18 @@
+
+ GeoQuiz
+ Canberra is the capital of Australia.
+ The Pacific Ocean is larger than
+ the Atlantic Ocean.
+ The Suez Canal connects the Red Sea
+ and the Indian Ocean.
+ The source of the Nile River is in Egypt.
+ The Amazon River is the longest river
+ in the Americas.
+ Lake Baikal is the world\'s oldest and deepest
+ freshwater lake.
+ True
+ False
+ Next
+ Correct!
+ Incorrect!
+
diff --git a/Book Files/02_MVC/GeoQuiz/app/src/main/res/values/styles.xml b/Book Files/02_MVC/GeoQuiz/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/Book Files/02_MVC/GeoQuiz/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Book Files/02_MVC/GeoQuiz/app/src/test/java/com/bignerdranch/android/geoquiz/ExampleUnitTest.java b/Book Files/02_MVC/GeoQuiz/app/src/test/java/com/bignerdranch/android/geoquiz/ExampleUnitTest.java
new file mode 100644
index 0000000..6b78649
--- /dev/null
+++ b/Book Files/02_MVC/GeoQuiz/app/src/test/java/com/bignerdranch/android/geoquiz/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.geoquiz;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/02_MVC/GeoQuiz/build.gradle b/Book Files/02_MVC/GeoQuiz/build.gradle
new file mode 100644
index 0000000..1ea4bd0
--- /dev/null
+++ b/Book Files/02_MVC/GeoQuiz/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Book Files/02_MVC/GeoQuiz/gradle.properties b/Book Files/02_MVC/GeoQuiz/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/Book Files/02_MVC/GeoQuiz/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/Book Files/02_MVC/GeoQuiz/gradle/wrapper/gradle-wrapper.jar b/Book Files/02_MVC/GeoQuiz/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/Book Files/02_MVC/GeoQuiz/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/02_MVC/GeoQuiz/gradle/wrapper/gradle-wrapper.properties b/Book Files/02_MVC/GeoQuiz/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a82392d
--- /dev/null
+++ b/Book Files/02_MVC/GeoQuiz/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/02_MVC/GeoQuiz/gradlew b/Book Files/02_MVC/GeoQuiz/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/Book Files/02_MVC/GeoQuiz/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/02_MVC/GeoQuiz/gradlew.bat b/Book Files/02_MVC/GeoQuiz/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/02_MVC/GeoQuiz/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/02_MVC/GeoQuiz/settings.gradle b/Book Files/02_MVC/GeoQuiz/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/02_MVC/GeoQuiz/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/02_MVC/LICENSE.txt b/Book Files/02_MVC/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Book Files/02_MVC/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Book Files/03_ActivityLifecycle/.DS_Store b/Book Files/03_ActivityLifecycle/.DS_Store
new file mode 100644
index 0000000..5008ddf
Binary files /dev/null and b/Book Files/03_ActivityLifecycle/.DS_Store differ
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/.gitignore b/Book Files/03_ActivityLifecycle/GeoQuiz/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/GeoQuiz/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/.idea/compiler.xml b/Book Files/03_ActivityLifecycle/GeoQuiz/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/GeoQuiz/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/.idea/copyright/profiles_settings.xml b/Book Files/03_ActivityLifecycle/GeoQuiz/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/GeoQuiz/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/.idea/encodings.xml b/Book Files/03_ActivityLifecycle/GeoQuiz/.idea/encodings.xml
new file mode 100644
index 0000000..97626ba
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/GeoQuiz/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/.idea/gradle.xml b/Book Files/03_ActivityLifecycle/GeoQuiz/.idea/gradle.xml
new file mode 100644
index 0000000..0e23f8e
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/GeoQuiz/.idea/gradle.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/.idea/misc.xml b/Book Files/03_ActivityLifecycle/GeoQuiz/.idea/misc.xml
new file mode 100644
index 0000000..fbb6828
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/GeoQuiz/.idea/misc.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/.idea/modules.xml b/Book Files/03_ActivityLifecycle/GeoQuiz/.idea/modules.xml
new file mode 100644
index 0000000..53041ba
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/GeoQuiz/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/.idea/runConfigurations.xml b/Book Files/03_ActivityLifecycle/GeoQuiz/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/GeoQuiz/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/app/.gitignore b/Book Files/03_ActivityLifecycle/GeoQuiz/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/GeoQuiz/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/app/build.gradle b/Book Files/03_ActivityLifecycle/GeoQuiz/app/build.gradle
new file mode 100644
index 0000000..a0284ac
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/GeoQuiz/app/build.gradle
@@ -0,0 +1,29 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ applicationId "com.bignerdranch.android.geoquiz"
+ minSdkVersion 19
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ testCompile 'junit:junit:4.12'
+}
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/app/proguard-rules.pro b/Book Files/03_ActivityLifecycle/GeoQuiz/app/proguard-rules.pro
new file mode 100644
index 0000000..99da964
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/GeoQuiz/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/chris/AndroidDeveloper/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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 *;
+#}
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/androidTest/java/com/bignerdranch/android/geoquiz/ExampleInstrumentedTest.java b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/androidTest/java/com/bignerdranch/android/geoquiz/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..68985dc
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/androidTest/java/com/bignerdranch/android/geoquiz/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.geoquiz;
+
+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.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.bignerdranch.android.geoquiz", appContext.getPackageName());
+ }
+}
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/AndroidManifest.xml b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..acb8d7a
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/AndroidManifest.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/Question.java b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/Question.java
new file mode 100644
index 0000000..b879043
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/Question.java
@@ -0,0 +1,28 @@
+package com.bignerdranch.android.geoquiz;
+
+public class Question {
+
+ private int mTextResId;
+ private boolean mAnswerTrue;
+
+ public Question(int textResId, boolean answerTrue) {
+ mTextResId = textResId;
+ mAnswerTrue = answerTrue;
+ }
+
+ public int getTextResId() {
+ return mTextResId;
+ }
+
+ public void setTextResId(int textResId) {
+ mTextResId = textResId;
+ }
+
+ public boolean isAnswerTrue() {
+ return mAnswerTrue;
+ }
+
+ public void setAnswerTrue(boolean answerTrue) {
+ mAnswerTrue = answerTrue;
+ }
+}
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/QuizActivity.java b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/QuizActivity.java
new file mode 100644
index 0000000..cb0593d
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/QuizActivity.java
@@ -0,0 +1,128 @@
+package com.bignerdranch.android.geoquiz;
+
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class QuizActivity extends AppCompatActivity {
+
+ private static final String TAG = "QuizActivity";
+ private static final String KEY_INDEX = "index";
+
+ private Button mTrueButton;
+ private Button mFalseButton;
+ private Button mNextButton;
+ private TextView mQuestionTextView;
+
+ private Question[] mQuestionBank = new Question[] {
+ new Question(R.string.question_australia, true),
+ new Question(R.string.question_oceans, true),
+ new Question(R.string.question_mideast, false),
+ new Question(R.string.question_africa, false),
+ new Question(R.string.question_americas, true),
+ new Question(R.string.question_asia, true),
+ };
+
+ private int mCurrentIndex = 0;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.d(TAG, "onCreate(Bundle) called");
+ setContentView(R.layout.activity_quiz);
+
+ if (savedInstanceState != null) {
+ mCurrentIndex = savedInstanceState.getInt(KEY_INDEX, 0);
+ }
+
+ mQuestionTextView = (TextView) findViewById(R.id.question_text_view);
+
+ mTrueButton = (Button) findViewById(R.id.true_button);
+ mTrueButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ checkAnswer(true);
+ }
+ });
+
+ mFalseButton = (Button) findViewById(R.id.false_button);
+ mFalseButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ checkAnswer(false);
+ }
+ });
+
+ mNextButton = (Button) findViewById(R.id.next_button);
+ mNextButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCurrentIndex = (mCurrentIndex + 1) % mQuestionBank.length;
+ updateQuestion();
+ }
+ });
+
+ updateQuestion();
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ Log.d(TAG, "onStart() called");
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ Log.d(TAG, "onResume() called");
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ Log.d(TAG, "onPause() called");
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle savedInstanceState) {
+ super.onSaveInstanceState(savedInstanceState);
+ Log.i(TAG, "onSaveInstanceState");
+ savedInstanceState.putInt(KEY_INDEX, mCurrentIndex);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ Log.d(TAG, "onStop() called");
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ Log.d(TAG, "onDestroy() called");
+ }
+
+ private void updateQuestion() {
+ int question = mQuestionBank[mCurrentIndex].getTextResId();
+ mQuestionTextView.setText(question);
+ }
+
+ private void checkAnswer(boolean userPressedTrue) {
+ boolean answerIsTrue = mQuestionBank[mCurrentIndex].isAnswerTrue();
+
+ int messageResId = 0;
+
+ if (userPressedTrue == answerIsTrue) {
+ messageResId = R.string.correct_toast;
+ } else {
+ messageResId = R.string.incorrect_toast;
+ }
+
+ Toast.makeText(this, messageResId, Toast.LENGTH_SHORT)
+ .show();
+ }
+}
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/drawable-hdpi/arrow_left.png b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/drawable-hdpi/arrow_left.png
new file mode 100644
index 0000000..60186ca
Binary files /dev/null and b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/drawable-hdpi/arrow_left.png differ
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/drawable-hdpi/arrow_right.png b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/drawable-hdpi/arrow_right.png
new file mode 100644
index 0000000..5eb216a
Binary files /dev/null and b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/drawable-hdpi/arrow_right.png differ
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/drawable-mdpi/arrow_left.png b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/drawable-mdpi/arrow_left.png
new file mode 100644
index 0000000..71c1a42
Binary files /dev/null and b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/drawable-mdpi/arrow_left.png differ
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/drawable-mdpi/arrow_right.png b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/drawable-mdpi/arrow_right.png
new file mode 100644
index 0000000..c505b45
Binary files /dev/null and b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/drawable-mdpi/arrow_right.png differ
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/drawable-xhdpi/arrow_left.png b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/drawable-xhdpi/arrow_left.png
new file mode 100644
index 0000000..7a48ab4
Binary files /dev/null and b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/drawable-xhdpi/arrow_left.png differ
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/drawable-xhdpi/arrow_right.png b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/drawable-xhdpi/arrow_right.png
new file mode 100644
index 0000000..eb411b8
Binary files /dev/null and b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/drawable-xhdpi/arrow_right.png differ
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/drawable-xxhdpi/arrow_left.png b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/drawable-xxhdpi/arrow_left.png
new file mode 100644
index 0000000..76d1e07
Binary files /dev/null and b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/drawable-xxhdpi/arrow_left.png differ
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/drawable-xxhdpi/arrow_right.png b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/drawable-xxhdpi/arrow_right.png
new file mode 100644
index 0000000..16115d8
Binary files /dev/null and b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/drawable-xxhdpi/arrow_right.png differ
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/layout-land/activity_quiz.xml b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/layout-land/activity_quiz.xml
new file mode 100644
index 0000000..435d894
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/layout-land/activity_quiz.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/layout/activity_quiz.xml b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/layout/activity_quiz.xml
new file mode 100644
index 0000000..27aed5f
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/layout/activity_quiz.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/values-w820dp/dimens.xml b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/values/colors.xml b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/values/dimens.xml b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/values/strings.xml b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..eac26c3
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/values/strings.xml
@@ -0,0 +1,18 @@
+
+ GeoQuiz
+ Canberra is the capital of Australia.
+ The Pacific Ocean is larger than
+ the Atlantic Ocean.
+ The Suez Canal connects the Red Sea
+ and the Indian Ocean.
+ The source of the Nile River is in Egypt.
+ The Amazon River is the longest river
+ in the Americas.
+ Lake Baikal is the world\'s oldest and deepest
+ freshwater lake.
+ True
+ False
+ Next
+ Correct!
+ Incorrect!
+
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/values/styles.xml b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/test/java/com/bignerdranch/android/geoquiz/ExampleUnitTest.java b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/test/java/com/bignerdranch/android/geoquiz/ExampleUnitTest.java
new file mode 100644
index 0000000..6b78649
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/GeoQuiz/app/src/test/java/com/bignerdranch/android/geoquiz/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.geoquiz;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/build.gradle b/Book Files/03_ActivityLifecycle/GeoQuiz/build.gradle
new file mode 100644
index 0000000..1ea4bd0
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/GeoQuiz/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/gradle.properties b/Book Files/03_ActivityLifecycle/GeoQuiz/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/GeoQuiz/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/gradle/wrapper/gradle-wrapper.jar b/Book Files/03_ActivityLifecycle/GeoQuiz/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/Book Files/03_ActivityLifecycle/GeoQuiz/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/gradle/wrapper/gradle-wrapper.properties b/Book Files/03_ActivityLifecycle/GeoQuiz/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a82392d
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/GeoQuiz/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/gradlew b/Book Files/03_ActivityLifecycle/GeoQuiz/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/GeoQuiz/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/gradlew.bat b/Book Files/03_ActivityLifecycle/GeoQuiz/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/GeoQuiz/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/03_ActivityLifecycle/GeoQuiz/settings.gradle b/Book Files/03_ActivityLifecycle/GeoQuiz/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/GeoQuiz/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/03_ActivityLifecycle/LICENSE.txt b/Book Files/03_ActivityLifecycle/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Book Files/03_ActivityLifecycle/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Book Files/05_SecondActivity/.gitignore b/Book Files/05_SecondActivity/.gitignore
new file mode 100644
index 0000000..0d77745
--- /dev/null
+++ b/Book Files/05_SecondActivity/.gitignore
@@ -0,0 +1,58 @@
+# Created by https://www.gitignore.io
+
+.DS_Store
+
+### Android ###
+# Built application files
+*.apk
+*.ap_
+
+# Files for the Dalvik VM
+*.dex
+
+# Java class files
+*.class
+
+# Generated files
+bin/
+gen/
+
+# Gradle files
+.gradle/
+build/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Proguard folder generated by Eclipse
+proguard/
+
+# Log Files
+*.log
+
+
+### Intellij ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
+
+/*.iml
+
+## Directory-based project format:
+.idea/
+
+## File-based project format:
+*.ipr
+*.iws
+
+## Plugin-specific files:
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
diff --git a/Book Files/05_SecondActivity/GeoQuiz/.gitignore b/Book Files/05_SecondActivity/GeoQuiz/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/Book Files/05_SecondActivity/GeoQuiz/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/.gitignore b/Book Files/05_SecondActivity/GeoQuiz/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/05_SecondActivity/GeoQuiz/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/build.gradle b/Book Files/05_SecondActivity/GeoQuiz/app/build.gradle
new file mode 100644
index 0000000..a0284ac
--- /dev/null
+++ b/Book Files/05_SecondActivity/GeoQuiz/app/build.gradle
@@ -0,0 +1,29 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ applicationId "com.bignerdranch.android.geoquiz"
+ minSdkVersion 19
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ testCompile 'junit:junit:4.12'
+}
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/proguard-rules.pro b/Book Files/05_SecondActivity/GeoQuiz/app/proguard-rules.pro
new file mode 100644
index 0000000..99da964
--- /dev/null
+++ b/Book Files/05_SecondActivity/GeoQuiz/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/chris/AndroidDeveloper/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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 *;
+#}
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/src/androidTest/java/com/bignerdranch/android/geoquiz/ExampleInstrumentedTest.java b/Book Files/05_SecondActivity/GeoQuiz/app/src/androidTest/java/com/bignerdranch/android/geoquiz/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..68985dc
--- /dev/null
+++ b/Book Files/05_SecondActivity/GeoQuiz/app/src/androidTest/java/com/bignerdranch/android/geoquiz/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.geoquiz;
+
+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.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.bignerdranch.android.geoquiz", appContext.getPackageName());
+ }
+}
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/src/main/AndroidManifest.xml b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..acd92bb
--- /dev/null
+++ b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/CheatActivity.java b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/CheatActivity.java
new file mode 100644
index 0000000..b0f13e4
--- /dev/null
+++ b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/CheatActivity.java
@@ -0,0 +1,61 @@
+package com.bignerdranch.android.geoquiz;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+public class CheatActivity extends AppCompatActivity {
+
+ private static final String EXTRA_ANSWER_IS_TRUE =
+ "com.bignerdranch.android.geoquiz.answer_is_true";
+ private static final String EXTRA_ANSWER_SHOWN =
+ "com.bignerdranch.android.geoquiz.answer_shown";
+
+ private boolean mAnswerIsTrue;
+
+ private TextView mAnswerTextView;
+ private Button mShowAnswerButton;
+
+ public static Intent newIntent(Context packageContext, boolean answerIsTrue) {
+ Intent intent = new Intent(packageContext, CheatActivity.class);
+ intent.putExtra(EXTRA_ANSWER_IS_TRUE, answerIsTrue);
+ return intent;
+ }
+
+ public static boolean wasAnswerShown(Intent result) {
+ return result.getBooleanExtra(EXTRA_ANSWER_SHOWN, false);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_cheat);
+
+ mAnswerIsTrue = getIntent().getBooleanExtra(EXTRA_ANSWER_IS_TRUE, false);
+
+ mAnswerTextView = (TextView) findViewById(R.id.answer_text_view);
+
+ mShowAnswerButton = (Button) findViewById(R.id.show_answer_button);
+ mShowAnswerButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mAnswerIsTrue) {
+ mAnswerTextView.setText(R.string.true_button);
+ } else {
+ mAnswerTextView.setText(R.string.false_button);
+ }
+ setAnswerShownResult(true);
+ }
+ });
+ }
+
+ private void setAnswerShownResult(boolean isAnswerShown) {
+ Intent data = new Intent();
+ data.putExtra(EXTRA_ANSWER_SHOWN, isAnswerShown);
+ setResult(RESULT_OK, data);
+ }
+}
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/Question.java b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/Question.java
new file mode 100644
index 0000000..b879043
--- /dev/null
+++ b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/Question.java
@@ -0,0 +1,28 @@
+package com.bignerdranch.android.geoquiz;
+
+public class Question {
+
+ private int mTextResId;
+ private boolean mAnswerTrue;
+
+ public Question(int textResId, boolean answerTrue) {
+ mTextResId = textResId;
+ mAnswerTrue = answerTrue;
+ }
+
+ public int getTextResId() {
+ return mTextResId;
+ }
+
+ public void setTextResId(int textResId) {
+ mTextResId = textResId;
+ }
+
+ public boolean isAnswerTrue() {
+ return mAnswerTrue;
+ }
+
+ public void setAnswerTrue(boolean answerTrue) {
+ mAnswerTrue = answerTrue;
+ }
+}
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/QuizActivity.java b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/QuizActivity.java
new file mode 100644
index 0000000..09a209b
--- /dev/null
+++ b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/QuizActivity.java
@@ -0,0 +1,162 @@
+package com.bignerdranch.android.geoquiz;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class QuizActivity extends AppCompatActivity {
+
+ private static final String TAG = "QuizActivity";
+ private static final String KEY_INDEX = "index";
+ private static final int REQUEST_CODE_CHEAT = 0;
+
+ private Button mTrueButton;
+ private Button mFalseButton;
+ private Button mNextButton;
+ private Button mCheatButton;
+ private TextView mQuestionTextView;
+
+ private Question[] mQuestionBank = new Question[] {
+ new Question(R.string.question_australia, true),
+ new Question(R.string.question_oceans, true),
+ new Question(R.string.question_mideast, false),
+ new Question(R.string.question_africa, false),
+ new Question(R.string.question_americas, true),
+ new Question(R.string.question_asia, true),
+ };
+
+ private int mCurrentIndex = 0;
+ private boolean mIsCheater;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.d(TAG, "onCreate(Bundle) called");
+ setContentView(R.layout.activity_quiz);
+
+ if (savedInstanceState != null) {
+ mCurrentIndex = savedInstanceState.getInt(KEY_INDEX, 0);
+ }
+
+ mQuestionTextView = (TextView) findViewById(R.id.question_text_view);
+
+ mTrueButton = (Button) findViewById(R.id.true_button);
+ mTrueButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ checkAnswer(true);
+ }
+ });
+
+ mFalseButton = (Button) findViewById(R.id.false_button);
+ mFalseButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ checkAnswer(false);
+ }
+ });
+
+ mNextButton = (Button) findViewById(R.id.next_button);
+ mNextButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCurrentIndex = (mCurrentIndex + 1) % mQuestionBank.length;
+ mIsCheater = false;
+ updateQuestion();
+ }
+ });
+
+ mCheatButton = (Button) findViewById(R.id.cheat_button);
+ mCheatButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ boolean answerIsTrue = mQuestionBank[mCurrentIndex].isAnswerTrue();
+ Intent intent = CheatActivity.newIntent(QuizActivity.this, answerIsTrue);
+ startActivityForResult(intent, REQUEST_CODE_CHEAT);
+ }
+ });
+
+ updateQuestion();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode != Activity.RESULT_OK) {
+ return;
+ }
+
+ if (requestCode == REQUEST_CODE_CHEAT) {
+ if (data == null) {
+ return;
+ }
+ mIsCheater = CheatActivity.wasAnswerShown(data);
+ }
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ Log.d(TAG, "onStart() called");
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ Log.d(TAG, "onResume() called");
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ Log.d(TAG, "onPause() called");
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle savedInstanceState) {
+ super.onSaveInstanceState(savedInstanceState);
+ Log.i(TAG, "onSaveInstanceState");
+ savedInstanceState.putInt(KEY_INDEX, mCurrentIndex);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ Log.d(TAG, "onStop() called");
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ Log.d(TAG, "onDestroy() called");
+ }
+
+ private void updateQuestion() {
+ int question = mQuestionBank[mCurrentIndex].getTextResId();
+ mQuestionTextView.setText(question);
+ }
+
+ private void checkAnswer(boolean userPressedTrue) {
+ boolean answerIsTrue = mQuestionBank[mCurrentIndex].isAnswerTrue();
+
+ int messageResId = 0;
+
+ if (mIsCheater) {
+ messageResId = R.string.judgment_toast;
+ } else {
+ if (userPressedTrue == answerIsTrue) {
+ messageResId = R.string.correct_toast;
+ } else {
+ messageResId = R.string.incorrect_toast;
+ }
+ }
+
+ Toast.makeText(this, messageResId, Toast.LENGTH_SHORT)
+ .show();
+ }
+}
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/drawable-hdpi/arrow_left.png b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/drawable-hdpi/arrow_left.png
new file mode 100644
index 0000000..60186ca
Binary files /dev/null and b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/drawable-hdpi/arrow_left.png differ
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/drawable-hdpi/arrow_right.png b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/drawable-hdpi/arrow_right.png
new file mode 100644
index 0000000..5eb216a
Binary files /dev/null and b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/drawable-hdpi/arrow_right.png differ
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/drawable-mdpi/arrow_left.png b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/drawable-mdpi/arrow_left.png
new file mode 100644
index 0000000..71c1a42
Binary files /dev/null and b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/drawable-mdpi/arrow_left.png differ
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/drawable-mdpi/arrow_right.png b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/drawable-mdpi/arrow_right.png
new file mode 100644
index 0000000..c505b45
Binary files /dev/null and b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/drawable-mdpi/arrow_right.png differ
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/drawable-xhdpi/arrow_left.png b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/drawable-xhdpi/arrow_left.png
new file mode 100644
index 0000000..7a48ab4
Binary files /dev/null and b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/drawable-xhdpi/arrow_left.png differ
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/drawable-xhdpi/arrow_right.png b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/drawable-xhdpi/arrow_right.png
new file mode 100644
index 0000000..eb411b8
Binary files /dev/null and b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/drawable-xhdpi/arrow_right.png differ
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/drawable-xxhdpi/arrow_left.png b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/drawable-xxhdpi/arrow_left.png
new file mode 100644
index 0000000..76d1e07
Binary files /dev/null and b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/drawable-xxhdpi/arrow_left.png differ
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/drawable-xxhdpi/arrow_right.png b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/drawable-xxhdpi/arrow_right.png
new file mode 100644
index 0000000..16115d8
Binary files /dev/null and b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/drawable-xxhdpi/arrow_right.png differ
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/layout-land/activity_quiz.xml b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/layout-land/activity_quiz.xml
new file mode 100644
index 0000000..f2996bd
--- /dev/null
+++ b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/layout-land/activity_quiz.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/layout/activity_cheat.xml b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/layout/activity_cheat.xml
new file mode 100644
index 0000000..731ce26
--- /dev/null
+++ b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/layout/activity_cheat.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/layout/activity_quiz.xml b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/layout/activity_quiz.xml
new file mode 100644
index 0000000..b880c65
--- /dev/null
+++ b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/layout/activity_quiz.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/values-w820dp/dimens.xml b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/values/colors.xml b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/values/dimens.xml b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/values/strings.xml b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..15b0498
--- /dev/null
+++ b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/values/strings.xml
@@ -0,0 +1,22 @@
+
+ GeoQuiz
+ Canberra is the capital of Australia.
+ The Pacific Ocean is larger than
+ the Atlantic Ocean.
+ The Suez Canal connects the Red Sea
+ and the Indian Ocean.
+ The source of the Nile River is in Egypt.
+ The Amazon River is the longest river
+ in the Americas.
+ Lake Baikal is the world\'s oldest and deepest
+ freshwater lake.
+ True
+ False
+ Next
+ Correct!
+ Incorrect!
+ Are you sure you want to do this?
+ Show Answer
+ Cheat!
+ Cheating is wrong.
+
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/values/styles.xml b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/Book Files/05_SecondActivity/GeoQuiz/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Book Files/05_SecondActivity/GeoQuiz/app/src/test/java/com/bignerdranch/android/geoquiz/ExampleUnitTest.java b/Book Files/05_SecondActivity/GeoQuiz/app/src/test/java/com/bignerdranch/android/geoquiz/ExampleUnitTest.java
new file mode 100644
index 0000000..6b78649
--- /dev/null
+++ b/Book Files/05_SecondActivity/GeoQuiz/app/src/test/java/com/bignerdranch/android/geoquiz/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.geoquiz;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/05_SecondActivity/GeoQuiz/build.gradle b/Book Files/05_SecondActivity/GeoQuiz/build.gradle
new file mode 100644
index 0000000..1ea4bd0
--- /dev/null
+++ b/Book Files/05_SecondActivity/GeoQuiz/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Book Files/05_SecondActivity/GeoQuiz/gradle.properties b/Book Files/05_SecondActivity/GeoQuiz/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/Book Files/05_SecondActivity/GeoQuiz/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/Book Files/05_SecondActivity/GeoQuiz/gradle/wrapper/gradle-wrapper.jar b/Book Files/05_SecondActivity/GeoQuiz/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/Book Files/05_SecondActivity/GeoQuiz/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/05_SecondActivity/GeoQuiz/gradle/wrapper/gradle-wrapper.properties b/Book Files/05_SecondActivity/GeoQuiz/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a82392d
--- /dev/null
+++ b/Book Files/05_SecondActivity/GeoQuiz/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/05_SecondActivity/GeoQuiz/gradlew b/Book Files/05_SecondActivity/GeoQuiz/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/Book Files/05_SecondActivity/GeoQuiz/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/05_SecondActivity/GeoQuiz/gradlew.bat b/Book Files/05_SecondActivity/GeoQuiz/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/05_SecondActivity/GeoQuiz/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/05_SecondActivity/GeoQuiz/settings.gradle b/Book Files/05_SecondActivity/GeoQuiz/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/05_SecondActivity/GeoQuiz/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/05_SecondActivity/LICENSE.txt b/Book Files/05_SecondActivity/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Book Files/05_SecondActivity/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Book Files/06_AndroidVersions/.DS_Store b/Book Files/06_AndroidVersions/.DS_Store
new file mode 100644
index 0000000..5008ddf
Binary files /dev/null and b/Book Files/06_AndroidVersions/.DS_Store differ
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/.gitignore b/Book Files/06_AndroidVersions/GeoQuiz/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/.idea/compiler.xml b/Book Files/06_AndroidVersions/GeoQuiz/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/.idea/copyright/profiles_settings.xml b/Book Files/06_AndroidVersions/GeoQuiz/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/.idea/encodings.xml b/Book Files/06_AndroidVersions/GeoQuiz/.idea/encodings.xml
new file mode 100644
index 0000000..97626ba
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/.idea/gradle.xml b/Book Files/06_AndroidVersions/GeoQuiz/.idea/gradle.xml
new file mode 100644
index 0000000..0e23f8e
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/.idea/gradle.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/.idea/misc.xml b/Book Files/06_AndroidVersions/GeoQuiz/.idea/misc.xml
new file mode 100644
index 0000000..5d19981
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/.idea/misc.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/.idea/modules.xml b/Book Files/06_AndroidVersions/GeoQuiz/.idea/modules.xml
new file mode 100644
index 0000000..53041ba
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/.idea/runConfigurations.xml b/Book Files/06_AndroidVersions/GeoQuiz/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/.gitignore b/Book Files/06_AndroidVersions/GeoQuiz/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/build.gradle b/Book Files/06_AndroidVersions/GeoQuiz/app/build.gradle
new file mode 100644
index 0000000..a0284ac
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/app/build.gradle
@@ -0,0 +1,29 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ applicationId "com.bignerdranch.android.geoquiz"
+ minSdkVersion 19
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ testCompile 'junit:junit:4.12'
+}
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/proguard-rules.pro b/Book Files/06_AndroidVersions/GeoQuiz/app/proguard-rules.pro
new file mode 100644
index 0000000..99da964
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/chris/AndroidDeveloper/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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 *;
+#}
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/src/androidTest/java/com/bignerdranch/android/geoquiz/ExampleInstrumentedTest.java b/Book Files/06_AndroidVersions/GeoQuiz/app/src/androidTest/java/com/bignerdranch/android/geoquiz/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..68985dc
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/app/src/androidTest/java/com/bignerdranch/android/geoquiz/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.geoquiz;
+
+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.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.bignerdranch.android.geoquiz", appContext.getPackageName());
+ }
+}
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/AndroidManifest.xml b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..acd92bb
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/CheatActivity.java b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/CheatActivity.java
new file mode 100644
index 0000000..6cd4409
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/CheatActivity.java
@@ -0,0 +1,83 @@
+package com.bignerdranch.android.geoquiz;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.View;
+import android.view.ViewAnimationUtils;
+import android.widget.Button;
+import android.widget.TextView;
+
+public class CheatActivity extends AppCompatActivity {
+
+ private static final String EXTRA_ANSWER_IS_TRUE =
+ "com.bignerdranch.android.geoquiz.answer_is_true";
+ private static final String EXTRA_ANSWER_SHOWN =
+ "com.bignerdranch.android.geoquiz.answer_shown";
+
+ private boolean mAnswerIsTrue;
+
+ private TextView mAnswerTextView;
+ private Button mShowAnswerButton;
+
+ public static Intent newIntent(Context packageContext, boolean answerIsTrue) {
+ Intent intent = new Intent(packageContext, CheatActivity.class);
+ intent.putExtra(EXTRA_ANSWER_IS_TRUE, answerIsTrue);
+ return intent;
+ }
+
+ public static boolean wasAnswerShown(Intent result) {
+ return result.getBooleanExtra(EXTRA_ANSWER_SHOWN, false);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_cheat);
+
+ mAnswerIsTrue = getIntent().getBooleanExtra(EXTRA_ANSWER_IS_TRUE, false);
+
+ mAnswerTextView = (TextView) findViewById(R.id.answer_text_view);
+
+ mShowAnswerButton = (Button) findViewById(R.id.show_answer_button);
+ mShowAnswerButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mAnswerIsTrue) {
+ mAnswerTextView.setText(R.string.true_button);
+ } else {
+ mAnswerTextView.setText(R.string.false_button);
+ }
+ setAnswerShownResult(true);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ int cx = mShowAnswerButton.getWidth() / 2;
+ int cy = mShowAnswerButton.getHeight() / 2;
+ float radius = mShowAnswerButton.getWidth();
+ Animator anim = ViewAnimationUtils
+ .createCircularReveal(mShowAnswerButton, cx, cy, radius, 0);
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ mShowAnswerButton.setVisibility(View.INVISIBLE);
+ }
+ });
+ anim.start();
+ } else {
+ mShowAnswerButton.setVisibility(View.INVISIBLE);
+ }
+ }
+ });
+ }
+
+ private void setAnswerShownResult(boolean isAnswerShown) {
+ Intent data = new Intent();
+ data.putExtra(EXTRA_ANSWER_SHOWN, isAnswerShown);
+ setResult(RESULT_OK, data);
+ }
+}
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/Question.java b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/Question.java
new file mode 100644
index 0000000..b879043
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/Question.java
@@ -0,0 +1,28 @@
+package com.bignerdranch.android.geoquiz;
+
+public class Question {
+
+ private int mTextResId;
+ private boolean mAnswerTrue;
+
+ public Question(int textResId, boolean answerTrue) {
+ mTextResId = textResId;
+ mAnswerTrue = answerTrue;
+ }
+
+ public int getTextResId() {
+ return mTextResId;
+ }
+
+ public void setTextResId(int textResId) {
+ mTextResId = textResId;
+ }
+
+ public boolean isAnswerTrue() {
+ return mAnswerTrue;
+ }
+
+ public void setAnswerTrue(boolean answerTrue) {
+ mAnswerTrue = answerTrue;
+ }
+}
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/QuizActivity.java b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/QuizActivity.java
new file mode 100644
index 0000000..09a209b
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/java/com/bignerdranch/android/geoquiz/QuizActivity.java
@@ -0,0 +1,162 @@
+package com.bignerdranch.android.geoquiz;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class QuizActivity extends AppCompatActivity {
+
+ private static final String TAG = "QuizActivity";
+ private static final String KEY_INDEX = "index";
+ private static final int REQUEST_CODE_CHEAT = 0;
+
+ private Button mTrueButton;
+ private Button mFalseButton;
+ private Button mNextButton;
+ private Button mCheatButton;
+ private TextView mQuestionTextView;
+
+ private Question[] mQuestionBank = new Question[] {
+ new Question(R.string.question_australia, true),
+ new Question(R.string.question_oceans, true),
+ new Question(R.string.question_mideast, false),
+ new Question(R.string.question_africa, false),
+ new Question(R.string.question_americas, true),
+ new Question(R.string.question_asia, true),
+ };
+
+ private int mCurrentIndex = 0;
+ private boolean mIsCheater;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.d(TAG, "onCreate(Bundle) called");
+ setContentView(R.layout.activity_quiz);
+
+ if (savedInstanceState != null) {
+ mCurrentIndex = savedInstanceState.getInt(KEY_INDEX, 0);
+ }
+
+ mQuestionTextView = (TextView) findViewById(R.id.question_text_view);
+
+ mTrueButton = (Button) findViewById(R.id.true_button);
+ mTrueButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ checkAnswer(true);
+ }
+ });
+
+ mFalseButton = (Button) findViewById(R.id.false_button);
+ mFalseButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ checkAnswer(false);
+ }
+ });
+
+ mNextButton = (Button) findViewById(R.id.next_button);
+ mNextButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCurrentIndex = (mCurrentIndex + 1) % mQuestionBank.length;
+ mIsCheater = false;
+ updateQuestion();
+ }
+ });
+
+ mCheatButton = (Button) findViewById(R.id.cheat_button);
+ mCheatButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ boolean answerIsTrue = mQuestionBank[mCurrentIndex].isAnswerTrue();
+ Intent intent = CheatActivity.newIntent(QuizActivity.this, answerIsTrue);
+ startActivityForResult(intent, REQUEST_CODE_CHEAT);
+ }
+ });
+
+ updateQuestion();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode != Activity.RESULT_OK) {
+ return;
+ }
+
+ if (requestCode == REQUEST_CODE_CHEAT) {
+ if (data == null) {
+ return;
+ }
+ mIsCheater = CheatActivity.wasAnswerShown(data);
+ }
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ Log.d(TAG, "onStart() called");
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ Log.d(TAG, "onResume() called");
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ Log.d(TAG, "onPause() called");
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle savedInstanceState) {
+ super.onSaveInstanceState(savedInstanceState);
+ Log.i(TAG, "onSaveInstanceState");
+ savedInstanceState.putInt(KEY_INDEX, mCurrentIndex);
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ Log.d(TAG, "onStop() called");
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ Log.d(TAG, "onDestroy() called");
+ }
+
+ private void updateQuestion() {
+ int question = mQuestionBank[mCurrentIndex].getTextResId();
+ mQuestionTextView.setText(question);
+ }
+
+ private void checkAnswer(boolean userPressedTrue) {
+ boolean answerIsTrue = mQuestionBank[mCurrentIndex].isAnswerTrue();
+
+ int messageResId = 0;
+
+ if (mIsCheater) {
+ messageResId = R.string.judgment_toast;
+ } else {
+ if (userPressedTrue == answerIsTrue) {
+ messageResId = R.string.correct_toast;
+ } else {
+ messageResId = R.string.incorrect_toast;
+ }
+ }
+
+ Toast.makeText(this, messageResId, Toast.LENGTH_SHORT)
+ .show();
+ }
+}
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/drawable-hdpi/arrow_left.png b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/drawable-hdpi/arrow_left.png
new file mode 100644
index 0000000..60186ca
Binary files /dev/null and b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/drawable-hdpi/arrow_left.png differ
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/drawable-hdpi/arrow_right.png b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/drawable-hdpi/arrow_right.png
new file mode 100644
index 0000000..5eb216a
Binary files /dev/null and b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/drawable-hdpi/arrow_right.png differ
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/drawable-mdpi/arrow_left.png b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/drawable-mdpi/arrow_left.png
new file mode 100644
index 0000000..71c1a42
Binary files /dev/null and b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/drawable-mdpi/arrow_left.png differ
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/drawable-mdpi/arrow_right.png b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/drawable-mdpi/arrow_right.png
new file mode 100644
index 0000000..c505b45
Binary files /dev/null and b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/drawable-mdpi/arrow_right.png differ
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/drawable-xhdpi/arrow_left.png b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/drawable-xhdpi/arrow_left.png
new file mode 100644
index 0000000..7a48ab4
Binary files /dev/null and b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/drawable-xhdpi/arrow_left.png differ
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/drawable-xhdpi/arrow_right.png b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/drawable-xhdpi/arrow_right.png
new file mode 100644
index 0000000..eb411b8
Binary files /dev/null and b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/drawable-xhdpi/arrow_right.png differ
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/drawable-xxhdpi/arrow_left.png b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/drawable-xxhdpi/arrow_left.png
new file mode 100644
index 0000000..76d1e07
Binary files /dev/null and b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/drawable-xxhdpi/arrow_left.png differ
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/drawable-xxhdpi/arrow_right.png b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/drawable-xxhdpi/arrow_right.png
new file mode 100644
index 0000000..16115d8
Binary files /dev/null and b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/drawable-xxhdpi/arrow_right.png differ
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/layout-land/activity_quiz.xml b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/layout-land/activity_quiz.xml
new file mode 100644
index 0000000..f2996bd
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/layout-land/activity_quiz.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/layout/activity_cheat.xml b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/layout/activity_cheat.xml
new file mode 100644
index 0000000..731ce26
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/layout/activity_cheat.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/layout/activity_quiz.xml b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/layout/activity_quiz.xml
new file mode 100644
index 0000000..b880c65
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/layout/activity_quiz.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/values-w820dp/dimens.xml b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/values/colors.xml b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/values/dimens.xml b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/values/strings.xml b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..15b0498
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/values/strings.xml
@@ -0,0 +1,22 @@
+
+ GeoQuiz
+ Canberra is the capital of Australia.
+ The Pacific Ocean is larger than
+ the Atlantic Ocean.
+ The Suez Canal connects the Red Sea
+ and the Indian Ocean.
+ The source of the Nile River is in Egypt.
+ The Amazon River is the longest river
+ in the Americas.
+ Lake Baikal is the world\'s oldest and deepest
+ freshwater lake.
+ True
+ False
+ Next
+ Correct!
+ Incorrect!
+ Are you sure you want to do this?
+ Show Answer
+ Cheat!
+ Cheating is wrong.
+
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/values/styles.xml b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/app/src/test/java/com/bignerdranch/android/geoquiz/ExampleUnitTest.java b/Book Files/06_AndroidVersions/GeoQuiz/app/src/test/java/com/bignerdranch/android/geoquiz/ExampleUnitTest.java
new file mode 100644
index 0000000..6b78649
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/app/src/test/java/com/bignerdranch/android/geoquiz/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.geoquiz;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/build.gradle b/Book Files/06_AndroidVersions/GeoQuiz/build.gradle
new file mode 100644
index 0000000..1ea4bd0
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/gradle.properties b/Book Files/06_AndroidVersions/GeoQuiz/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/gradle/wrapper/gradle-wrapper.jar b/Book Files/06_AndroidVersions/GeoQuiz/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/Book Files/06_AndroidVersions/GeoQuiz/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/gradle/wrapper/gradle-wrapper.properties b/Book Files/06_AndroidVersions/GeoQuiz/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a82392d
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/gradlew b/Book Files/06_AndroidVersions/GeoQuiz/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/gradlew.bat b/Book Files/06_AndroidVersions/GeoQuiz/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/06_AndroidVersions/GeoQuiz/settings.gradle b/Book Files/06_AndroidVersions/GeoQuiz/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/06_AndroidVersions/GeoQuiz/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/06_AndroidVersions/LICENSE.txt b/Book Files/06_AndroidVersions/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Book Files/06_AndroidVersions/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Book Files/07_UIFragments/.gitignore b/Book Files/07_UIFragments/.gitignore
new file mode 100644
index 0000000..0d77745
--- /dev/null
+++ b/Book Files/07_UIFragments/.gitignore
@@ -0,0 +1,58 @@
+# Created by https://www.gitignore.io
+
+.DS_Store
+
+### Android ###
+# Built application files
+*.apk
+*.ap_
+
+# Files for the Dalvik VM
+*.dex
+
+# Java class files
+*.class
+
+# Generated files
+bin/
+gen/
+
+# Gradle files
+.gradle/
+build/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Proguard folder generated by Eclipse
+proguard/
+
+# Log Files
+*.log
+
+
+### Intellij ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
+
+/*.iml
+
+## Directory-based project format:
+.idea/
+
+## File-based project format:
+*.ipr
+*.iws
+
+## Plugin-specific files:
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
diff --git a/Book Files/07_UIFragments/CriminalIntent/.gitignore b/Book Files/07_UIFragments/CriminalIntent/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/Book Files/07_UIFragments/CriminalIntent/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Book Files/07_UIFragments/CriminalIntent/app/.gitignore b/Book Files/07_UIFragments/CriminalIntent/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/07_UIFragments/CriminalIntent/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/07_UIFragments/CriminalIntent/app/build.gradle b/Book Files/07_UIFragments/CriminalIntent/app/build.gradle
new file mode 100644
index 0000000..01ebad3
--- /dev/null
+++ b/Book Files/07_UIFragments/CriminalIntent/app/build.gradle
@@ -0,0 +1,29 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ applicationId "com.bignerdranch.android.criminalintent"
+ minSdkVersion 19
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ testCompile 'junit:junit:4.12'
+}
diff --git a/Book Files/07_UIFragments/CriminalIntent/app/proguard-rules.pro b/Book Files/07_UIFragments/CriminalIntent/app/proguard-rules.pro
new file mode 100644
index 0000000..99da964
--- /dev/null
+++ b/Book Files/07_UIFragments/CriminalIntent/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/chris/AndroidDeveloper/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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 *;
+#}
diff --git a/Book Files/07_UIFragments/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java b/Book Files/07_UIFragments/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..3ed35e7
--- /dev/null
+++ b/Book Files/07_UIFragments/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.criminalintent;
+
+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.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.bignerdranch.android.criminalintent", appContext.getPackageName());
+ }
+}
diff --git a/Book Files/07_UIFragments/CriminalIntent/app/src/main/AndroidManifest.xml b/Book Files/07_UIFragments/CriminalIntent/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..858007e
--- /dev/null
+++ b/Book Files/07_UIFragments/CriminalIntent/app/src/main/AndroidManifest.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/07_UIFragments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java b/Book Files/07_UIFragments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java
new file mode 100644
index 0000000..cfa8b3d
--- /dev/null
+++ b/Book Files/07_UIFragments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java
@@ -0,0 +1,45 @@
+package com.bignerdranch.android.criminalintent;
+
+import java.util.Date;
+import java.util.UUID;
+
+public class Crime {
+
+ private UUID mId;
+ private String mTitle;
+ private Date mDate;
+ private boolean mSolved;
+
+ public Crime() {
+ mId = UUID.randomUUID();
+ mDate = new Date();
+ }
+
+ public UUID getId() {
+ return mId;
+ }
+
+ public String getTitle() {
+ return mTitle;
+ }
+
+ public void setTitle(String title) {
+ mTitle = title;
+ }
+
+ public Date getDate() {
+ return mDate;
+ }
+
+ public void setDate(Date date) {
+ mDate = date;
+ }
+
+ public boolean isSolved() {
+ return mSolved;
+ }
+
+ public void setSolved(boolean solved) {
+ mSolved = solved;
+ }
+}
diff --git a/Book Files/07_UIFragments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeActivity.java b/Book Files/07_UIFragments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeActivity.java
new file mode 100644
index 0000000..e58224b
--- /dev/null
+++ b/Book Files/07_UIFragments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeActivity.java
@@ -0,0 +1,25 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.AppCompatActivity;
+
+public class CrimeActivity extends AppCompatActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_crime);
+
+ FragmentManager fm = getSupportFragmentManager();
+ Fragment fragment = fm.findFragmentById(R.id.fragment_container);
+
+ if (fragment == null) {
+ fragment = new CrimeFragment();
+ fm.beginTransaction()
+ .add(R.id.fragment_container, fragment)
+ .commit();
+ }
+ }
+}
diff --git a/Book Files/07_UIFragments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java b/Book Files/07_UIFragments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java
new file mode 100644
index 0000000..10c6ac2
--- /dev/null
+++ b/Book Files/07_UIFragments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java
@@ -0,0 +1,67 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+
+import static android.widget.CompoundButton.*;
+
+public class CrimeFragment extends Fragment {
+
+ private Crime mCrime;
+ private EditText mTitleField;
+ private Button mDateButton;
+ private CheckBox mSolvedCheckbox;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mCrime = new Crime();
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.fragment_crime, container, false);
+
+ mTitleField = (EditText) v.findViewById(R.id.crime_title);
+ mTitleField.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ mCrime.setTitle(s.toString());
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+
+ }
+ });
+
+ mDateButton = (Button) v.findViewById(R.id.crime_date);
+ mDateButton.setText(mCrime.getDate().toString());
+ mDateButton.setEnabled(false);
+
+ mSolvedCheckbox = (CheckBox) v.findViewById(R.id.crime_solved);
+ mSolvedCheckbox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView,
+ boolean isChecked) {
+ mCrime.setSolved(isChecked);
+ }
+ });
+
+ return v;
+ }
+}
diff --git a/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/layout/activity_crime.xml b/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/layout/activity_crime.xml
new file mode 100644
index 0000000..1773143
--- /dev/null
+++ b/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/layout/activity_crime.xml
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/layout/fragment_crime.xml b/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/layout/fragment_crime.xml
new file mode 100644
index 0000000..20734cf
--- /dev/null
+++ b/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/layout/fragment_crime.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml b/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/values/colors.xml b/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/values/dimens.xml b/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/values/strings.xml b/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..edd8d55
--- /dev/null
+++ b/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/values/strings.xml
@@ -0,0 +1,7 @@
+
+ CriminalIntent
+ Enter a title for the crime.
+ Title
+ Details
+ Solved
+
diff --git a/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/values/styles.xml b/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/Book Files/07_UIFragments/CriminalIntent/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Book Files/07_UIFragments/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java b/Book Files/07_UIFragments/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java
new file mode 100644
index 0000000..e0e2773
--- /dev/null
+++ b/Book Files/07_UIFragments/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.criminalintent;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/07_UIFragments/CriminalIntent/build.gradle b/Book Files/07_UIFragments/CriminalIntent/build.gradle
new file mode 100644
index 0000000..1ea4bd0
--- /dev/null
+++ b/Book Files/07_UIFragments/CriminalIntent/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Book Files/07_UIFragments/CriminalIntent/gradle.properties b/Book Files/07_UIFragments/CriminalIntent/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/Book Files/07_UIFragments/CriminalIntent/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/Book Files/07_UIFragments/CriminalIntent/gradle/wrapper/gradle-wrapper.jar b/Book Files/07_UIFragments/CriminalIntent/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/Book Files/07_UIFragments/CriminalIntent/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/07_UIFragments/CriminalIntent/gradle/wrapper/gradle-wrapper.properties b/Book Files/07_UIFragments/CriminalIntent/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a82392d
--- /dev/null
+++ b/Book Files/07_UIFragments/CriminalIntent/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/07_UIFragments/CriminalIntent/gradlew b/Book Files/07_UIFragments/CriminalIntent/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/Book Files/07_UIFragments/CriminalIntent/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/07_UIFragments/CriminalIntent/gradlew.bat b/Book Files/07_UIFragments/CriminalIntent/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/07_UIFragments/CriminalIntent/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/07_UIFragments/CriminalIntent/settings.gradle b/Book Files/07_UIFragments/CriminalIntent/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/07_UIFragments/CriminalIntent/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/07_UIFragments/LICENSE.txt b/Book Files/07_UIFragments/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Book Files/07_UIFragments/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Book Files/08_RecyclerView/.DS_Store b/Book Files/08_RecyclerView/.DS_Store
new file mode 100644
index 0000000..8ee919e
Binary files /dev/null and b/Book Files/08_RecyclerView/.DS_Store differ
diff --git a/Book Files/08_RecyclerView/CriminalIntent/.gitignore b/Book Files/08_RecyclerView/CriminalIntent/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Book Files/08_RecyclerView/CriminalIntent/.idea/compiler.xml b/Book Files/08_RecyclerView/CriminalIntent/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/08_RecyclerView/CriminalIntent/.idea/copyright/profiles_settings.xml b/Book Files/08_RecyclerView/CriminalIntent/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Book Files/08_RecyclerView/CriminalIntent/.idea/encodings.xml b/Book Files/08_RecyclerView/CriminalIntent/.idea/encodings.xml
new file mode 100644
index 0000000..97626ba
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/08_RecyclerView/CriminalIntent/.idea/gradle.xml b/Book Files/08_RecyclerView/CriminalIntent/.idea/gradle.xml
new file mode 100644
index 0000000..0e23f8e
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/.idea/gradle.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/08_RecyclerView/CriminalIntent/.idea/misc.xml b/Book Files/08_RecyclerView/CriminalIntent/.idea/misc.xml
new file mode 100644
index 0000000..5d19981
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/.idea/misc.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/08_RecyclerView/CriminalIntent/.idea/modules.xml b/Book Files/08_RecyclerView/CriminalIntent/.idea/modules.xml
new file mode 100644
index 0000000..37be0dd
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/08_RecyclerView/CriminalIntent/.idea/runConfigurations.xml b/Book Files/08_RecyclerView/CriminalIntent/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/08_RecyclerView/CriminalIntent/app/.gitignore b/Book Files/08_RecyclerView/CriminalIntent/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/08_RecyclerView/CriminalIntent/app/build.gradle b/Book Files/08_RecyclerView/CriminalIntent/app/build.gradle
new file mode 100644
index 0000000..be1c179
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/app/build.gradle
@@ -0,0 +1,30 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ applicationId "com.bignerdranch.android.criminalintent"
+ minSdkVersion 19
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ testCompile 'junit:junit:4.12'
+ compile 'com.android.support:recyclerview-v7:25.3.0'
+}
diff --git a/Book Files/08_RecyclerView/CriminalIntent/app/proguard-rules.pro b/Book Files/08_RecyclerView/CriminalIntent/app/proguard-rules.pro
new file mode 100644
index 0000000..99da964
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/chris/AndroidDeveloper/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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 *;
+#}
diff --git a/Book Files/08_RecyclerView/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java b/Book Files/08_RecyclerView/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..3ed35e7
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.criminalintent;
+
+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.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.bignerdranch.android.criminalintent", appContext.getPackageName());
+ }
+}
diff --git a/Book Files/08_RecyclerView/CriminalIntent/app/src/main/AndroidManifest.xml b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..6be2eb0
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/08_RecyclerView/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java
new file mode 100644
index 0000000..cfa8b3d
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java
@@ -0,0 +1,45 @@
+package com.bignerdranch.android.criminalintent;
+
+import java.util.Date;
+import java.util.UUID;
+
+public class Crime {
+
+ private UUID mId;
+ private String mTitle;
+ private Date mDate;
+ private boolean mSolved;
+
+ public Crime() {
+ mId = UUID.randomUUID();
+ mDate = new Date();
+ }
+
+ public UUID getId() {
+ return mId;
+ }
+
+ public String getTitle() {
+ return mTitle;
+ }
+
+ public void setTitle(String title) {
+ mTitle = title;
+ }
+
+ public Date getDate() {
+ return mDate;
+ }
+
+ public void setDate(Date date) {
+ mDate = date;
+ }
+
+ public boolean isSolved() {
+ return mSolved;
+ }
+
+ public void setSolved(boolean solved) {
+ mSolved = solved;
+ }
+}
diff --git a/Book Files/08_RecyclerView/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeActivity.java b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeActivity.java
new file mode 100644
index 0000000..67a9e8a
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeActivity.java
@@ -0,0 +1,11 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.support.v4.app.Fragment;
+
+public class CrimeActivity extends SingleFragmentActivity {
+
+ @Override
+ protected Fragment createFragment() {
+ return new CrimeFragment();
+ }
+}
diff --git a/Book Files/08_RecyclerView/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java
new file mode 100644
index 0000000..62f2641
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java
@@ -0,0 +1,68 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+
+import static android.widget.CompoundButton.*;
+
+public class CrimeFragment extends Fragment {
+
+ private Crime mCrime;
+ private EditText mTitleField;
+ private Button mDateButton;
+ private CheckBox mSolvedCheckbox;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mCrime = new Crime();
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.fragment_crime, container, false);
+
+ mTitleField = (EditText) v.findViewById(R.id.crime_title);
+ mTitleField.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ mCrime.setTitle(s.toString());
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+
+ }
+ });
+
+ mDateButton = (Button) v.findViewById(R.id.crime_date);
+ mDateButton.setText(mCrime.getDate().toString());
+ mDateButton.setEnabled(false);
+
+ mSolvedCheckbox = (CheckBox) v.findViewById(R.id.crime_solved);
+ mSolvedCheckbox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView,
+ boolean isChecked) {
+ mCrime.setSolved(isChecked);
+ }
+ });
+
+ return v;
+ }
+}
diff --git a/Book Files/08_RecyclerView/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java
new file mode 100644
index 0000000..1679b63
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java
@@ -0,0 +1,45 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.content.Context;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+public class CrimeLab {
+ private static CrimeLab sCrimeLab;
+
+ private List mCrimes;
+
+ public static CrimeLab get(Context context) {
+ if (sCrimeLab == null) {
+ sCrimeLab = new CrimeLab(context);
+ }
+
+ return sCrimeLab;
+ }
+
+ private CrimeLab(Context context) {
+ mCrimes = new ArrayList<>();
+ for (int i = 0; i < 100; i++) {
+ Crime crime = new Crime();
+ crime.setTitle("Crime #" + i);
+ crime.setSolved(i % 2 == 0);
+ mCrimes.add(crime);
+ }
+ }
+
+ public List getCrimes() {
+ return mCrimes;
+ }
+
+ public Crime getCrime(UUID id) {
+ for (Crime crime : mCrimes) {
+ if (crime.getId().equals(id)) {
+ return crime;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/Book Files/08_RecyclerView/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java
new file mode 100644
index 0000000..25557d2
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java
@@ -0,0 +1,11 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.support.v4.app.Fragment;
+
+public class CrimeListActivity extends SingleFragmentActivity {
+
+ @Override
+ protected Fragment createFragment() {
+ return new CrimeListFragment();
+ }
+}
diff --git a/Book Files/08_RecyclerView/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java
new file mode 100644
index 0000000..4c06f25
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java
@@ -0,0 +1,96 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.util.List;
+
+public class CrimeListFragment extends Fragment {
+ private RecyclerView mCrimeRecyclerView;
+ private CrimeAdapter mAdapter;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_crime_list, container, false);
+
+ mCrimeRecyclerView = (RecyclerView) view
+ .findViewById(R.id.crime_recycler_view);
+ mCrimeRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
+
+ updateUI();
+
+ return view;
+ }
+
+ private void updateUI() {
+ CrimeLab crimeLab = CrimeLab.get(getActivity());
+ List crimes = crimeLab.getCrimes();
+
+ mAdapter = new CrimeAdapter(crimes);
+ mCrimeRecyclerView.setAdapter(mAdapter);
+ }
+
+ private class CrimeHolder extends RecyclerView.ViewHolder
+ implements View.OnClickListener {
+
+ private Crime mCrime;
+
+ private TextView mTitleTextView;
+ private TextView mDateTextView;
+
+ public CrimeHolder(LayoutInflater inflater, ViewGroup parent) {
+ super(inflater.inflate(R.layout.list_item_crime, parent, false));
+ itemView.setOnClickListener(this);
+
+ mTitleTextView = (TextView) itemView.findViewById(R.id.crime_title);
+ mDateTextView = (TextView) itemView.findViewById(R.id.crime_date);
+ }
+
+ public void bind(Crime crime) {
+ mCrime = crime;
+ mTitleTextView.setText(mCrime.getTitle());
+ mDateTextView.setText(mCrime.getDate().toString());
+ }
+
+ @Override
+ public void onClick(View view) {
+ Toast.makeText(getActivity(),
+ mCrime.getTitle() + " clicked!", Toast.LENGTH_SHORT)
+ .show();
+ }
+ }
+
+ private class CrimeAdapter extends RecyclerView.Adapter {
+
+ private List mCrimes;
+
+ public CrimeAdapter(List crimes) {
+ mCrimes = crimes;
+ }
+
+ @Override
+ public CrimeHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
+ return new CrimeHolder(layoutInflater, parent);
+ }
+
+ @Override
+ public void onBindViewHolder(CrimeHolder holder, int position) {
+ Crime crime = mCrimes.get(position);
+ holder.bind(crime);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mCrimes.size();
+ }
+ }
+}
diff --git a/Book Files/08_RecyclerView/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java
new file mode 100644
index 0000000..9e1d2d8
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java
@@ -0,0 +1,27 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.AppCompatActivity;
+
+public abstract class SingleFragmentActivity extends AppCompatActivity {
+
+ protected abstract Fragment createFragment();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_fragment);
+
+ FragmentManager fm = getSupportFragmentManager();
+ Fragment fragment = fm.findFragmentById(R.id.fragment_container);
+
+ if (fragment == null) {
+ fragment = createFragment();
+ fm.beginTransaction()
+ .add(R.id.fragment_container, fragment)
+ .commit();
+ }
+ }
+}
diff --git a/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/layout/activity_fragment.xml b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/layout/activity_fragment.xml
new file mode 100644
index 0000000..1773143
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/layout/activity_fragment.xml
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/layout/fragment_crime.xml b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/layout/fragment_crime.xml
new file mode 100644
index 0000000..20734cf
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/layout/fragment_crime.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml
new file mode 100644
index 0000000..4d23b7c
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml
@@ -0,0 +1,5 @@
+
+
\ No newline at end of file
diff --git a/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/layout/list_item_crime.xml b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/layout/list_item_crime.xml
new file mode 100644
index 0000000..4f7e23a
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/layout/list_item_crime.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/values/colors.xml b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/values/dimens.xml b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/values/strings.xml b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..edd8d55
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/values/strings.xml
@@ -0,0 +1,7 @@
+
+ CriminalIntent
+ Enter a title for the crime.
+ Title
+ Details
+ Solved
+
diff --git a/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/values/styles.xml b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Book Files/08_RecyclerView/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java b/Book Files/08_RecyclerView/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java
new file mode 100644
index 0000000..e0e2773
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.criminalintent;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/08_RecyclerView/CriminalIntent/build.gradle b/Book Files/08_RecyclerView/CriminalIntent/build.gradle
new file mode 100644
index 0000000..1ea4bd0
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Book Files/08_RecyclerView/CriminalIntent/gradle.properties b/Book Files/08_RecyclerView/CriminalIntent/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/Book Files/08_RecyclerView/CriminalIntent/gradle/wrapper/gradle-wrapper.jar b/Book Files/08_RecyclerView/CriminalIntent/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/Book Files/08_RecyclerView/CriminalIntent/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/08_RecyclerView/CriminalIntent/gradle/wrapper/gradle-wrapper.properties b/Book Files/08_RecyclerView/CriminalIntent/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a82392d
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/08_RecyclerView/CriminalIntent/gradlew b/Book Files/08_RecyclerView/CriminalIntent/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/08_RecyclerView/CriminalIntent/gradlew.bat b/Book Files/08_RecyclerView/CriminalIntent/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/08_RecyclerView/CriminalIntent/settings.gradle b/Book Files/08_RecyclerView/CriminalIntent/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/08_RecyclerView/CriminalIntent/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/08_RecyclerView/LICENSE.txt b/Book Files/08_RecyclerView/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Book Files/08_RecyclerView/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Book Files/09_LayoutsAndWidgets/.DS_Store b/Book Files/09_LayoutsAndWidgets/.DS_Store
new file mode 100644
index 0000000..57dd42a
Binary files /dev/null and b/Book Files/09_LayoutsAndWidgets/.DS_Store differ
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/.gitignore b/Book Files/09_LayoutsAndWidgets/CriminalIntent/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/.idea/compiler.xml b/Book Files/09_LayoutsAndWidgets/CriminalIntent/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/.idea/copyright/profiles_settings.xml b/Book Files/09_LayoutsAndWidgets/CriminalIntent/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/.idea/encodings.xml b/Book Files/09_LayoutsAndWidgets/CriminalIntent/.idea/encodings.xml
new file mode 100644
index 0000000..97626ba
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/.idea/gradle.xml b/Book Files/09_LayoutsAndWidgets/CriminalIntent/.idea/gradle.xml
new file mode 100644
index 0000000..7ac24c7
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/.idea/gradle.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/.idea/misc.xml b/Book Files/09_LayoutsAndWidgets/CriminalIntent/.idea/misc.xml
new file mode 100644
index 0000000..fbb6828
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/.idea/misc.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/.idea/modules.xml b/Book Files/09_LayoutsAndWidgets/CriminalIntent/.idea/modules.xml
new file mode 100644
index 0000000..37be0dd
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/.idea/runConfigurations.xml b/Book Files/09_LayoutsAndWidgets/CriminalIntent/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/.gitignore b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/build.gradle b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/build.gradle
new file mode 100644
index 0000000..d05c462
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/build.gradle
@@ -0,0 +1,32 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ applicationId "com.bignerdranch.android.criminalintent"
+ minSdkVersion 19
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ testCompile 'junit:junit:4.12'
+ compile 'com.android.support:support-v4:25.3.0'
+ compile 'com.android.support:recyclerview-v7:25.3.0'
+ compile 'com.android.support.constraint:constraint-layout:1.0.2'
+}
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/proguard-rules.pro b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/proguard-rules.pro
new file mode 100644
index 0000000..99da964
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/chris/AndroidDeveloper/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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 *;
+#}
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..3ed35e7
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.criminalintent;
+
+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.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.bignerdranch.android.criminalintent", appContext.getPackageName());
+ }
+}
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/AndroidManifest.xml b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..6be2eb0
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java
new file mode 100644
index 0000000..cfa8b3d
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java
@@ -0,0 +1,45 @@
+package com.bignerdranch.android.criminalintent;
+
+import java.util.Date;
+import java.util.UUID;
+
+public class Crime {
+
+ private UUID mId;
+ private String mTitle;
+ private Date mDate;
+ private boolean mSolved;
+
+ public Crime() {
+ mId = UUID.randomUUID();
+ mDate = new Date();
+ }
+
+ public UUID getId() {
+ return mId;
+ }
+
+ public String getTitle() {
+ return mTitle;
+ }
+
+ public void setTitle(String title) {
+ mTitle = title;
+ }
+
+ public Date getDate() {
+ return mDate;
+ }
+
+ public void setDate(Date date) {
+ mDate = date;
+ }
+
+ public boolean isSolved() {
+ return mSolved;
+ }
+
+ public void setSolved(boolean solved) {
+ mSolved = solved;
+ }
+}
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeActivity.java b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeActivity.java
new file mode 100644
index 0000000..67a9e8a
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeActivity.java
@@ -0,0 +1,11 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.support.v4.app.Fragment;
+
+public class CrimeActivity extends SingleFragmentActivity {
+
+ @Override
+ protected Fragment createFragment() {
+ return new CrimeFragment();
+ }
+}
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java
new file mode 100644
index 0000000..62f2641
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java
@@ -0,0 +1,68 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+
+import static android.widget.CompoundButton.*;
+
+public class CrimeFragment extends Fragment {
+
+ private Crime mCrime;
+ private EditText mTitleField;
+ private Button mDateButton;
+ private CheckBox mSolvedCheckbox;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mCrime = new Crime();
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.fragment_crime, container, false);
+
+ mTitleField = (EditText) v.findViewById(R.id.crime_title);
+ mTitleField.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ mCrime.setTitle(s.toString());
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+
+ }
+ });
+
+ mDateButton = (Button) v.findViewById(R.id.crime_date);
+ mDateButton.setText(mCrime.getDate().toString());
+ mDateButton.setEnabled(false);
+
+ mSolvedCheckbox = (CheckBox) v.findViewById(R.id.crime_solved);
+ mSolvedCheckbox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView,
+ boolean isChecked) {
+ mCrime.setSolved(isChecked);
+ }
+ });
+
+ return v;
+ }
+}
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java
new file mode 100644
index 0000000..1679b63
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java
@@ -0,0 +1,45 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.content.Context;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+public class CrimeLab {
+ private static CrimeLab sCrimeLab;
+
+ private List mCrimes;
+
+ public static CrimeLab get(Context context) {
+ if (sCrimeLab == null) {
+ sCrimeLab = new CrimeLab(context);
+ }
+
+ return sCrimeLab;
+ }
+
+ private CrimeLab(Context context) {
+ mCrimes = new ArrayList<>();
+ for (int i = 0; i < 100; i++) {
+ Crime crime = new Crime();
+ crime.setTitle("Crime #" + i);
+ crime.setSolved(i % 2 == 0);
+ mCrimes.add(crime);
+ }
+ }
+
+ public List getCrimes() {
+ return mCrimes;
+ }
+
+ public Crime getCrime(UUID id) {
+ for (Crime crime : mCrimes) {
+ if (crime.getId().equals(id)) {
+ return crime;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java
new file mode 100644
index 0000000..25557d2
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java
@@ -0,0 +1,11 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.support.v4.app.Fragment;
+
+public class CrimeListActivity extends SingleFragmentActivity {
+
+ @Override
+ protected Fragment createFragment() {
+ return new CrimeListFragment();
+ }
+}
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java
new file mode 100644
index 0000000..a7bde0d
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java
@@ -0,0 +1,100 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import java.util.List;
+
+public class CrimeListFragment extends Fragment {
+ private RecyclerView mCrimeRecyclerView;
+ private CrimeAdapter mAdapter;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_crime_list, container, false);
+
+ mCrimeRecyclerView = (RecyclerView) view
+ .findViewById(R.id.crime_recycler_view);
+ mCrimeRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
+
+ updateUI();
+
+ return view;
+ }
+
+ private void updateUI() {
+ CrimeLab crimeLab = CrimeLab.get(getActivity());
+ List crimes = crimeLab.getCrimes();
+
+ mAdapter = new CrimeAdapter(crimes);
+ mCrimeRecyclerView.setAdapter(mAdapter);
+ }
+
+ private class CrimeHolder extends RecyclerView.ViewHolder
+ implements View.OnClickListener {
+
+ private Crime mCrime;
+
+ private TextView mTitleTextView;
+ private TextView mDateTextView;
+ private ImageView mSolvedImageView;
+
+ public CrimeHolder(LayoutInflater inflater, ViewGroup parent) {
+ super(inflater.inflate(R.layout.list_item_crime, parent, false));
+ itemView.setOnClickListener(this);
+
+ mTitleTextView = (TextView) itemView.findViewById(R.id.crime_title);
+ mDateTextView = (TextView) itemView.findViewById(R.id.crime_date);
+ mSolvedImageView = (ImageView) itemView.findViewById(R.id.crime_solved);
+ }
+
+ public void bind(Crime crime) {
+ mCrime = crime;
+ mTitleTextView.setText(mCrime.getTitle());
+ mDateTextView.setText(mCrime.getDate().toString());
+ mSolvedImageView.setVisibility(crime.isSolved() ? View.VISIBLE : View.GONE);
+ }
+
+ @Override
+ public void onClick(View view) {
+ Toast.makeText(getActivity(),
+ mCrime.getTitle() + " clicked!", Toast.LENGTH_SHORT)
+ .show();
+ }
+ }
+
+ private class CrimeAdapter extends RecyclerView.Adapter {
+
+ private List mCrimes;
+
+ public CrimeAdapter(List crimes) {
+ mCrimes = crimes;
+ }
+
+ @Override
+ public CrimeHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
+ return new CrimeHolder(layoutInflater, parent);
+ }
+
+ @Override
+ public void onBindViewHolder(CrimeHolder holder, int position) {
+ Crime crime = mCrimes.get(position);
+ holder.bind(crime);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mCrimes.size();
+ }
+ }
+}
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java
new file mode 100644
index 0000000..9e1d2d8
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java
@@ -0,0 +1,27 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.AppCompatActivity;
+
+public abstract class SingleFragmentActivity extends AppCompatActivity {
+
+ protected abstract Fragment createFragment();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_fragment);
+
+ FragmentManager fm = getSupportFragmentManager();
+ Fragment fragment = fm.findFragmentById(R.id.fragment_container);
+
+ if (fragment == null) {
+ fragment = createFragment();
+ fm.beginTransaction()
+ .add(R.id.fragment_container, fragment)
+ .commit();
+ }
+ }
+}
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/drawable-hdpi/ic_solved.png b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/drawable-hdpi/ic_solved.png
new file mode 100644
index 0000000..e9b9263
Binary files /dev/null and b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/drawable-hdpi/ic_solved.png differ
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/drawable-mdpi/ic_solved.png b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/drawable-mdpi/ic_solved.png
new file mode 100644
index 0000000..9548d3c
Binary files /dev/null and b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/drawable-mdpi/ic_solved.png differ
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_solved.png b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_solved.png
new file mode 100644
index 0000000..6ab438d
Binary files /dev/null and b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_solved.png differ
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_solved.png b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_solved.png
new file mode 100644
index 0000000..ca05191
Binary files /dev/null and b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_solved.png differ
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/drawable-xxxhdpi/ic_solved.png b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/drawable-xxxhdpi/ic_solved.png
new file mode 100644
index 0000000..15d3cf4
Binary files /dev/null and b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/drawable-xxxhdpi/ic_solved.png differ
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/layout/activity_fragment.xml b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/layout/activity_fragment.xml
new file mode 100644
index 0000000..1773143
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/layout/activity_fragment.xml
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/layout/fragment_crime.xml b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/layout/fragment_crime.xml
new file mode 100644
index 0000000..20734cf
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/layout/fragment_crime.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml
new file mode 100644
index 0000000..4d23b7c
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml
@@ -0,0 +1,5 @@
+
+
\ No newline at end of file
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/layout/list_item_crime.xml b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/layout/list_item_crime.xml
new file mode 100644
index 0000000..e149593
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/layout/list_item_crime.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/values/colors.xml b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/values/dimens.xml b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/values/strings.xml b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..edd8d55
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/values/strings.xml
@@ -0,0 +1,7 @@
+
+ CriminalIntent
+ Enter a title for the crime.
+ Title
+ Details
+ Solved
+
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/values/styles.xml b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java
new file mode 100644
index 0000000..e0e2773
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.criminalintent;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/build.gradle b/Book Files/09_LayoutsAndWidgets/CriminalIntent/build.gradle
new file mode 100644
index 0000000..1ea4bd0
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/gradle.properties b/Book Files/09_LayoutsAndWidgets/CriminalIntent/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/gradle/wrapper/gradle-wrapper.jar b/Book Files/09_LayoutsAndWidgets/CriminalIntent/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/Book Files/09_LayoutsAndWidgets/CriminalIntent/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/gradle/wrapper/gradle-wrapper.properties b/Book Files/09_LayoutsAndWidgets/CriminalIntent/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a82392d
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/gradlew b/Book Files/09_LayoutsAndWidgets/CriminalIntent/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/gradlew.bat b/Book Files/09_LayoutsAndWidgets/CriminalIntent/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/09_LayoutsAndWidgets/CriminalIntent/settings.gradle b/Book Files/09_LayoutsAndWidgets/CriminalIntent/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/CriminalIntent/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/09_LayoutsAndWidgets/LICENSE.txt b/Book Files/09_LayoutsAndWidgets/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Book Files/09_LayoutsAndWidgets/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Book Files/10_FragmentArguments/.DS_Store b/Book Files/10_FragmentArguments/.DS_Store
new file mode 100644
index 0000000..5008ddf
Binary files /dev/null and b/Book Files/10_FragmentArguments/.DS_Store differ
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/.gitignore b/Book Files/10_FragmentArguments/CriminalIntent/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/.idea/compiler.xml b/Book Files/10_FragmentArguments/CriminalIntent/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/.idea/copyright/profiles_settings.xml b/Book Files/10_FragmentArguments/CriminalIntent/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/.idea/encodings.xml b/Book Files/10_FragmentArguments/CriminalIntent/.idea/encodings.xml
new file mode 100644
index 0000000..97626ba
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/.idea/gradle.xml b/Book Files/10_FragmentArguments/CriminalIntent/.idea/gradle.xml
new file mode 100644
index 0000000..4819d5a
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/.idea/gradle.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/.idea/misc.xml b/Book Files/10_FragmentArguments/CriminalIntent/.idea/misc.xml
new file mode 100644
index 0000000..5d19981
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/.idea/misc.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/.idea/modules.xml b/Book Files/10_FragmentArguments/CriminalIntent/.idea/modules.xml
new file mode 100644
index 0000000..37be0dd
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/.idea/runConfigurations.xml b/Book Files/10_FragmentArguments/CriminalIntent/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/.gitignore b/Book Files/10_FragmentArguments/CriminalIntent/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/build.gradle b/Book Files/10_FragmentArguments/CriminalIntent/app/build.gradle
new file mode 100644
index 0000000..d05c462
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/app/build.gradle
@@ -0,0 +1,32 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ applicationId "com.bignerdranch.android.criminalintent"
+ minSdkVersion 19
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ testCompile 'junit:junit:4.12'
+ compile 'com.android.support:support-v4:25.3.0'
+ compile 'com.android.support:recyclerview-v7:25.3.0'
+ compile 'com.android.support.constraint:constraint-layout:1.0.2'
+}
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/proguard-rules.pro b/Book Files/10_FragmentArguments/CriminalIntent/app/proguard-rules.pro
new file mode 100644
index 0000000..99da964
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/chris/AndroidDeveloper/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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 *;
+#}
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java b/Book Files/10_FragmentArguments/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..3ed35e7
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.criminalintent;
+
+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.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.bignerdranch.android.criminalintent", appContext.getPackageName());
+ }
+}
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/AndroidManifest.xml b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..6be2eb0
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java
new file mode 100644
index 0000000..cfa8b3d
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java
@@ -0,0 +1,45 @@
+package com.bignerdranch.android.criminalintent;
+
+import java.util.Date;
+import java.util.UUID;
+
+public class Crime {
+
+ private UUID mId;
+ private String mTitle;
+ private Date mDate;
+ private boolean mSolved;
+
+ public Crime() {
+ mId = UUID.randomUUID();
+ mDate = new Date();
+ }
+
+ public UUID getId() {
+ return mId;
+ }
+
+ public String getTitle() {
+ return mTitle;
+ }
+
+ public void setTitle(String title) {
+ mTitle = title;
+ }
+
+ public Date getDate() {
+ return mDate;
+ }
+
+ public void setDate(Date date) {
+ mDate = date;
+ }
+
+ public boolean isSolved() {
+ return mSolved;
+ }
+
+ public void setSolved(boolean solved) {
+ mSolved = solved;
+ }
+}
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeActivity.java b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeActivity.java
new file mode 100644
index 0000000..53d0cc4
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeActivity.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.v4.app.Fragment;
+
+import java.util.UUID;
+
+public class CrimeActivity extends SingleFragmentActivity {
+
+ private static final String EXTRA_CRIME_ID =
+ "com.bignerdranch.android.criminalintent.crime_id";
+
+ public static Intent newIntent(Context packageContext, UUID crimeId) {
+ Intent intent = new Intent(packageContext, CrimeActivity.class);
+ intent.putExtra(EXTRA_CRIME_ID, crimeId);
+ return intent;
+ }
+
+ @Override
+ protected Fragment createFragment() {
+ UUID crimeId = (UUID) getIntent()
+ .getSerializableExtra(EXTRA_CRIME_ID);
+ return CrimeFragment.newInstance(crimeId);
+ }
+}
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java
new file mode 100644
index 0000000..3cfb02e
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java
@@ -0,0 +1,84 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+
+import java.util.UUID;
+
+import static android.widget.CompoundButton.*;
+
+public class CrimeFragment extends Fragment {
+
+ private static final String ARG_CRIME_ID = "crime_id";
+
+ private Crime mCrime;
+ private EditText mTitleField;
+ private Button mDateButton;
+ private CheckBox mSolvedCheckbox;
+
+ public static CrimeFragment newInstance(UUID crimeId) {
+ Bundle args = new Bundle();
+ args.putSerializable(ARG_CRIME_ID, crimeId);
+
+ CrimeFragment fragment = new CrimeFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ UUID crimeId = (UUID) getArguments().getSerializable(ARG_CRIME_ID);
+ mCrime = CrimeLab.get(getActivity()).getCrime(crimeId);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.fragment_crime, container, false);
+
+ mTitleField = (EditText) v.findViewById(R.id.crime_title);
+ mTitleField.setText(mCrime.getTitle());
+ mTitleField.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ mCrime.setTitle(s.toString());
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+
+ }
+ });
+
+ mDateButton = (Button) v.findViewById(R.id.crime_date);
+ mDateButton.setText(mCrime.getDate().toString());
+ mDateButton.setEnabled(false);
+
+ mSolvedCheckbox = (CheckBox) v.findViewById(R.id.crime_solved);
+ mSolvedCheckbox.setChecked(mCrime.isSolved());
+ mSolvedCheckbox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView,
+ boolean isChecked) {
+ mCrime.setSolved(isChecked);
+ }
+ });
+
+ return v;
+ }
+}
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java
new file mode 100644
index 0000000..1679b63
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java
@@ -0,0 +1,45 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.content.Context;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+public class CrimeLab {
+ private static CrimeLab sCrimeLab;
+
+ private List mCrimes;
+
+ public static CrimeLab get(Context context) {
+ if (sCrimeLab == null) {
+ sCrimeLab = new CrimeLab(context);
+ }
+
+ return sCrimeLab;
+ }
+
+ private CrimeLab(Context context) {
+ mCrimes = new ArrayList<>();
+ for (int i = 0; i < 100; i++) {
+ Crime crime = new Crime();
+ crime.setTitle("Crime #" + i);
+ crime.setSolved(i % 2 == 0);
+ mCrimes.add(crime);
+ }
+ }
+
+ public List getCrimes() {
+ return mCrimes;
+ }
+
+ public Crime getCrime(UUID id) {
+ for (Crime crime : mCrimes) {
+ if (crime.getId().equals(id)) {
+ return crime;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java
new file mode 100644
index 0000000..25557d2
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java
@@ -0,0 +1,11 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.support.v4.app.Fragment;
+
+public class CrimeListActivity extends SingleFragmentActivity {
+
+ @Override
+ protected Fragment createFragment() {
+ return new CrimeListFragment();
+ }
+}
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java
new file mode 100644
index 0000000..6c446b7
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java
@@ -0,0 +1,109 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.List;
+
+public class CrimeListFragment extends Fragment {
+ private RecyclerView mCrimeRecyclerView;
+ private CrimeAdapter mAdapter;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_crime_list, container, false);
+
+ mCrimeRecyclerView = (RecyclerView) view
+ .findViewById(R.id.crime_recycler_view);
+ mCrimeRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
+
+ updateUI();
+
+ return view;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ updateUI();
+ }
+
+ private void updateUI() {
+ CrimeLab crimeLab = CrimeLab.get(getActivity());
+ List crimes = crimeLab.getCrimes();
+
+ if (mAdapter == null) {
+ mAdapter = new CrimeAdapter(crimes);
+ mCrimeRecyclerView.setAdapter(mAdapter);
+ } else {
+ mAdapter.notifyDataSetChanged();
+ }
+ }
+
+ private class CrimeHolder extends RecyclerView.ViewHolder
+ implements View.OnClickListener {
+
+ private Crime mCrime;
+
+ private TextView mTitleTextView;
+ private TextView mDateTextView;
+ private ImageView mSolvedImageView;
+
+ public CrimeHolder(LayoutInflater inflater, ViewGroup parent) {
+ super(inflater.inflate(R.layout.list_item_crime, parent, false));
+ itemView.setOnClickListener(this);
+
+ mTitleTextView = (TextView) itemView.findViewById(R.id.crime_title);
+ mDateTextView = (TextView) itemView.findViewById(R.id.crime_date);
+ mSolvedImageView = (ImageView) itemView.findViewById(R.id.crime_solved);
+ }
+
+ public void bind(Crime crime) {
+ mCrime = crime;
+ mTitleTextView.setText(mCrime.getTitle());
+ mDateTextView.setText(mCrime.getDate().toString());
+ mSolvedImageView.setVisibility(crime.isSolved() ? View.VISIBLE : View.GONE);
+ }
+
+ @Override
+ public void onClick(View view) {
+ Intent intent = CrimeActivity.newIntent(getActivity(), mCrime.getId());
+ startActivity(intent);
+ }
+ }
+
+ private class CrimeAdapter extends RecyclerView.Adapter {
+
+ private List mCrimes;
+
+ public CrimeAdapter(List crimes) {
+ mCrimes = crimes;
+ }
+
+ @Override
+ public CrimeHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
+ return new CrimeHolder(layoutInflater, parent);
+ }
+
+ @Override
+ public void onBindViewHolder(CrimeHolder holder, int position) {
+ Crime crime = mCrimes.get(position);
+ holder.bind(crime);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mCrimes.size();
+ }
+ }
+}
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java
new file mode 100644
index 0000000..9e1d2d8
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java
@@ -0,0 +1,27 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.AppCompatActivity;
+
+public abstract class SingleFragmentActivity extends AppCompatActivity {
+
+ protected abstract Fragment createFragment();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_fragment);
+
+ FragmentManager fm = getSupportFragmentManager();
+ Fragment fragment = fm.findFragmentById(R.id.fragment_container);
+
+ if (fragment == null) {
+ fragment = createFragment();
+ fm.beginTransaction()
+ .add(R.id.fragment_container, fragment)
+ .commit();
+ }
+ }
+}
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/drawable-hdpi/ic_solved.png b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/drawable-hdpi/ic_solved.png
new file mode 100644
index 0000000..e9b9263
Binary files /dev/null and b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/drawable-hdpi/ic_solved.png differ
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/drawable-mdpi/ic_solved.png b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/drawable-mdpi/ic_solved.png
new file mode 100644
index 0000000..9548d3c
Binary files /dev/null and b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/drawable-mdpi/ic_solved.png differ
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_solved.png b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_solved.png
new file mode 100644
index 0000000..6ab438d
Binary files /dev/null and b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_solved.png differ
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_solved.png b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_solved.png
new file mode 100644
index 0000000..ca05191
Binary files /dev/null and b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_solved.png differ
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/drawable-xxxhdpi/ic_solved.png b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/drawable-xxxhdpi/ic_solved.png
new file mode 100644
index 0000000..15d3cf4
Binary files /dev/null and b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/drawable-xxxhdpi/ic_solved.png differ
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/layout/activity_fragment.xml b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/layout/activity_fragment.xml
new file mode 100644
index 0000000..1773143
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/layout/activity_fragment.xml
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/layout/fragment_crime.xml b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/layout/fragment_crime.xml
new file mode 100644
index 0000000..20734cf
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/layout/fragment_crime.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml
new file mode 100644
index 0000000..4d23b7c
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml
@@ -0,0 +1,5 @@
+
+
\ No newline at end of file
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/layout/list_item_crime.xml b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/layout/list_item_crime.xml
new file mode 100644
index 0000000..e149593
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/layout/list_item_crime.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/values/colors.xml b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/values/dimens.xml b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/values/strings.xml b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..edd8d55
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/values/strings.xml
@@ -0,0 +1,7 @@
+
+ CriminalIntent
+ Enter a title for the crime.
+ Title
+ Details
+ Solved
+
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/values/styles.xml b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java b/Book Files/10_FragmentArguments/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java
new file mode 100644
index 0000000..e0e2773
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.criminalintent;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/build.gradle b/Book Files/10_FragmentArguments/CriminalIntent/build.gradle
new file mode 100644
index 0000000..1ea4bd0
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/gradle.properties b/Book Files/10_FragmentArguments/CriminalIntent/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/gradle/wrapper/gradle-wrapper.jar b/Book Files/10_FragmentArguments/CriminalIntent/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/Book Files/10_FragmentArguments/CriminalIntent/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/gradle/wrapper/gradle-wrapper.properties b/Book Files/10_FragmentArguments/CriminalIntent/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a82392d
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/gradlew b/Book Files/10_FragmentArguments/CriminalIntent/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/gradlew.bat b/Book Files/10_FragmentArguments/CriminalIntent/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/10_FragmentArguments/CriminalIntent/settings.gradle b/Book Files/10_FragmentArguments/CriminalIntent/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/10_FragmentArguments/CriminalIntent/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/10_FragmentArguments/LICENSE.txt b/Book Files/10_FragmentArguments/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Book Files/10_FragmentArguments/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Book Files/11_ViewPager/.DS_Store b/Book Files/11_ViewPager/.DS_Store
new file mode 100755
index 0000000..5008ddf
Binary files /dev/null and b/Book Files/11_ViewPager/.DS_Store differ
diff --git a/Book Files/11_ViewPager/CriminalIntent/.gitignore b/Book Files/11_ViewPager/CriminalIntent/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Book Files/11_ViewPager/CriminalIntent/.idea/compiler.xml b/Book Files/11_ViewPager/CriminalIntent/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/11_ViewPager/CriminalIntent/.idea/copyright/profiles_settings.xml b/Book Files/11_ViewPager/CriminalIntent/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Book Files/11_ViewPager/CriminalIntent/.idea/encodings.xml b/Book Files/11_ViewPager/CriminalIntent/.idea/encodings.xml
new file mode 100644
index 0000000..97626ba
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/11_ViewPager/CriminalIntent/.idea/gradle.xml b/Book Files/11_ViewPager/CriminalIntent/.idea/gradle.xml
new file mode 100644
index 0000000..4819d5a
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/.idea/gradle.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/11_ViewPager/CriminalIntent/.idea/misc.xml b/Book Files/11_ViewPager/CriminalIntent/.idea/misc.xml
new file mode 100644
index 0000000..5d19981
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/.idea/misc.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/11_ViewPager/CriminalIntent/.idea/modules.xml b/Book Files/11_ViewPager/CriminalIntent/.idea/modules.xml
new file mode 100644
index 0000000..37be0dd
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/11_ViewPager/CriminalIntent/.idea/runConfigurations.xml b/Book Files/11_ViewPager/CriminalIntent/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/.gitignore b/Book Files/11_ViewPager/CriminalIntent/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/build.gradle b/Book Files/11_ViewPager/CriminalIntent/app/build.gradle
new file mode 100644
index 0000000..d05c462
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/app/build.gradle
@@ -0,0 +1,32 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ applicationId "com.bignerdranch.android.criminalintent"
+ minSdkVersion 19
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ testCompile 'junit:junit:4.12'
+ compile 'com.android.support:support-v4:25.3.0'
+ compile 'com.android.support:recyclerview-v7:25.3.0'
+ compile 'com.android.support.constraint:constraint-layout:1.0.2'
+}
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/proguard-rules.pro b/Book Files/11_ViewPager/CriminalIntent/app/proguard-rules.pro
new file mode 100644
index 0000000..99da964
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/chris/AndroidDeveloper/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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 *;
+#}
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java b/Book Files/11_ViewPager/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..3ed35e7
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.criminalintent;
+
+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.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.bignerdranch.android.criminalintent", appContext.getPackageName());
+ }
+}
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/main/AndroidManifest.xml b/Book Files/11_ViewPager/CriminalIntent/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..74975a6
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/app/src/main/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java b/Book Files/11_ViewPager/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java
new file mode 100644
index 0000000..cfa8b3d
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java
@@ -0,0 +1,45 @@
+package com.bignerdranch.android.criminalintent;
+
+import java.util.Date;
+import java.util.UUID;
+
+public class Crime {
+
+ private UUID mId;
+ private String mTitle;
+ private Date mDate;
+ private boolean mSolved;
+
+ public Crime() {
+ mId = UUID.randomUUID();
+ mDate = new Date();
+ }
+
+ public UUID getId() {
+ return mId;
+ }
+
+ public String getTitle() {
+ return mTitle;
+ }
+
+ public void setTitle(String title) {
+ mTitle = title;
+ }
+
+ public Date getDate() {
+ return mDate;
+ }
+
+ public void setDate(Date date) {
+ mDate = date;
+ }
+
+ public boolean isSolved() {
+ return mSolved;
+ }
+
+ public void setSolved(boolean solved) {
+ mSolved = solved;
+ }
+}
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java b/Book Files/11_ViewPager/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java
new file mode 100644
index 0000000..3cfb02e
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java
@@ -0,0 +1,84 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+
+import java.util.UUID;
+
+import static android.widget.CompoundButton.*;
+
+public class CrimeFragment extends Fragment {
+
+ private static final String ARG_CRIME_ID = "crime_id";
+
+ private Crime mCrime;
+ private EditText mTitleField;
+ private Button mDateButton;
+ private CheckBox mSolvedCheckbox;
+
+ public static CrimeFragment newInstance(UUID crimeId) {
+ Bundle args = new Bundle();
+ args.putSerializable(ARG_CRIME_ID, crimeId);
+
+ CrimeFragment fragment = new CrimeFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ UUID crimeId = (UUID) getArguments().getSerializable(ARG_CRIME_ID);
+ mCrime = CrimeLab.get(getActivity()).getCrime(crimeId);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.fragment_crime, container, false);
+
+ mTitleField = (EditText) v.findViewById(R.id.crime_title);
+ mTitleField.setText(mCrime.getTitle());
+ mTitleField.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ mCrime.setTitle(s.toString());
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+
+ }
+ });
+
+ mDateButton = (Button) v.findViewById(R.id.crime_date);
+ mDateButton.setText(mCrime.getDate().toString());
+ mDateButton.setEnabled(false);
+
+ mSolvedCheckbox = (CheckBox) v.findViewById(R.id.crime_solved);
+ mSolvedCheckbox.setChecked(mCrime.isSolved());
+ mSolvedCheckbox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView,
+ boolean isChecked) {
+ mCrime.setSolved(isChecked);
+ }
+ });
+
+ return v;
+ }
+}
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java b/Book Files/11_ViewPager/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java
new file mode 100644
index 0000000..1679b63
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java
@@ -0,0 +1,45 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.content.Context;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+public class CrimeLab {
+ private static CrimeLab sCrimeLab;
+
+ private List mCrimes;
+
+ public static CrimeLab get(Context context) {
+ if (sCrimeLab == null) {
+ sCrimeLab = new CrimeLab(context);
+ }
+
+ return sCrimeLab;
+ }
+
+ private CrimeLab(Context context) {
+ mCrimes = new ArrayList<>();
+ for (int i = 0; i < 100; i++) {
+ Crime crime = new Crime();
+ crime.setTitle("Crime #" + i);
+ crime.setSolved(i % 2 == 0);
+ mCrimes.add(crime);
+ }
+ }
+
+ public List getCrimes() {
+ return mCrimes;
+ }
+
+ public Crime getCrime(UUID id) {
+ for (Crime crime : mCrimes) {
+ if (crime.getId().equals(id)) {
+ return crime;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java b/Book Files/11_ViewPager/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java
new file mode 100644
index 0000000..25557d2
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java
@@ -0,0 +1,11 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.support.v4.app.Fragment;
+
+public class CrimeListActivity extends SingleFragmentActivity {
+
+ @Override
+ protected Fragment createFragment() {
+ return new CrimeListFragment();
+ }
+}
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java b/Book Files/11_ViewPager/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java
new file mode 100644
index 0000000..43c5601
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java
@@ -0,0 +1,109 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.List;
+
+public class CrimeListFragment extends Fragment {
+ private RecyclerView mCrimeRecyclerView;
+ private CrimeAdapter mAdapter;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_crime_list, container, false);
+
+ mCrimeRecyclerView = (RecyclerView) view
+ .findViewById(R.id.crime_recycler_view);
+ mCrimeRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
+
+ updateUI();
+
+ return view;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ updateUI();
+ }
+
+ private void updateUI() {
+ CrimeLab crimeLab = CrimeLab.get(getActivity());
+ List crimes = crimeLab.getCrimes();
+
+ if (mAdapter == null) {
+ mAdapter = new CrimeAdapter(crimes);
+ mCrimeRecyclerView.setAdapter(mAdapter);
+ } else {
+ mAdapter.notifyDataSetChanged();
+ }
+ }
+
+ private class CrimeHolder extends RecyclerView.ViewHolder
+ implements View.OnClickListener {
+
+ private Crime mCrime;
+
+ private TextView mTitleTextView;
+ private TextView mDateTextView;
+ private ImageView mSolvedImageView;
+
+ public CrimeHolder(LayoutInflater inflater, ViewGroup parent) {
+ super(inflater.inflate(R.layout.list_item_crime, parent, false));
+ itemView.setOnClickListener(this);
+
+ mTitleTextView = (TextView) itemView.findViewById(R.id.crime_title);
+ mDateTextView = (TextView) itemView.findViewById(R.id.crime_date);
+ mSolvedImageView = (ImageView) itemView.findViewById(R.id.crime_solved);
+ }
+
+ public void bind(Crime crime) {
+ mCrime = crime;
+ mTitleTextView.setText(mCrime.getTitle());
+ mDateTextView.setText(mCrime.getDate().toString());
+ mSolvedImageView.setVisibility(crime.isSolved() ? View.VISIBLE : View.GONE);
+ }
+
+ @Override
+ public void onClick(View view) {
+ Intent intent = CrimePagerActivity.newIntent(getActivity(), mCrime.getId());
+ startActivity(intent);
+ }
+ }
+
+ private class CrimeAdapter extends RecyclerView.Adapter {
+
+ private List mCrimes;
+
+ public CrimeAdapter(List crimes) {
+ mCrimes = crimes;
+ }
+
+ @Override
+ public CrimeHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
+ return new CrimeHolder(layoutInflater, parent);
+ }
+
+ @Override
+ public void onBindViewHolder(CrimeHolder holder, int position) {
+ Crime crime = mCrimes.get(position);
+ holder.bind(crime);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mCrimes.size();
+ }
+ }
+}
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimePagerActivity.java b/Book Files/11_ViewPager/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimePagerActivity.java
new file mode 100644
index 0000000..b11cca9
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimePagerActivity.java
@@ -0,0 +1,62 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentStatePagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.v7.app.AppCompatActivity;
+
+import java.util.List;
+import java.util.UUID;
+
+public class CrimePagerActivity extends AppCompatActivity {
+
+ private static final String EXTRA_CRIME_ID =
+ "com.bignerdranch.android.criminalintent.crime_id";
+
+ private ViewPager mViewPager;
+ private List mCrimes;
+
+ public static Intent newIntent(Context packageContext, UUID crimeId) {
+ Intent intent = new Intent(packageContext, CrimePagerActivity.class);
+ intent.putExtra(EXTRA_CRIME_ID, crimeId);
+ return intent;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_crime_pager);
+
+ UUID crimeId = (UUID) getIntent()
+ .getSerializableExtra(EXTRA_CRIME_ID);
+
+ mViewPager = (ViewPager) findViewById(R.id.crime_view_pager);
+
+ mCrimes = CrimeLab.get(this).getCrimes();
+ FragmentManager fragmentManager = getSupportFragmentManager();
+ mViewPager.setAdapter(new FragmentStatePagerAdapter(fragmentManager) {
+
+ @Override
+ public Fragment getItem(int position) {
+ Crime crime = mCrimes.get(position);
+ return CrimeFragment.newInstance(crime.getId());
+ }
+
+ @Override
+ public int getCount() {
+ return mCrimes.size();
+ }
+ });
+
+ for (int i = 0; i < mCrimes.size(); i++) {
+ if (mCrimes.get(i).getId().equals(crimeId)) {
+ mViewPager.setCurrentItem(i);
+ break;
+ }
+ }
+ }
+}
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java b/Book Files/11_ViewPager/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java
new file mode 100644
index 0000000..9e1d2d8
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java
@@ -0,0 +1,27 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.AppCompatActivity;
+
+public abstract class SingleFragmentActivity extends AppCompatActivity {
+
+ protected abstract Fragment createFragment();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_fragment);
+
+ FragmentManager fm = getSupportFragmentManager();
+ Fragment fragment = fm.findFragmentById(R.id.fragment_container);
+
+ if (fragment == null) {
+ fragment = createFragment();
+ fm.beginTransaction()
+ .add(R.id.fragment_container, fragment)
+ .commit();
+ }
+ }
+}
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/drawable-hdpi/ic_solved.png b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/drawable-hdpi/ic_solved.png
new file mode 100644
index 0000000..e9b9263
Binary files /dev/null and b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/drawable-hdpi/ic_solved.png differ
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/drawable-mdpi/ic_solved.png b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/drawable-mdpi/ic_solved.png
new file mode 100644
index 0000000..9548d3c
Binary files /dev/null and b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/drawable-mdpi/ic_solved.png differ
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_solved.png b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_solved.png
new file mode 100644
index 0000000..6ab438d
Binary files /dev/null and b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_solved.png differ
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_solved.png b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_solved.png
new file mode 100644
index 0000000..ca05191
Binary files /dev/null and b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_solved.png differ
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/drawable-xxxhdpi/ic_solved.png b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/drawable-xxxhdpi/ic_solved.png
new file mode 100644
index 0000000..15d3cf4
Binary files /dev/null and b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/drawable-xxxhdpi/ic_solved.png differ
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/layout/activity_crime_pager.xml b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/layout/activity_crime_pager.xml
new file mode 100644
index 0000000..a80c096
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/layout/activity_crime_pager.xml
@@ -0,0 +1,5 @@
+
+
\ No newline at end of file
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/layout/activity_fragment.xml b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/layout/activity_fragment.xml
new file mode 100644
index 0000000..1773143
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/layout/activity_fragment.xml
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/layout/fragment_crime.xml b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/layout/fragment_crime.xml
new file mode 100644
index 0000000..20734cf
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/layout/fragment_crime.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml
new file mode 100644
index 0000000..4d23b7c
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml
@@ -0,0 +1,5 @@
+
+
\ No newline at end of file
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/layout/list_item_crime.xml b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/layout/list_item_crime.xml
new file mode 100644
index 0000000..e149593
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/layout/list_item_crime.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/values/colors.xml b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/values/dimens.xml b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/values/strings.xml b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..edd8d55
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/values/strings.xml
@@ -0,0 +1,7 @@
+
+ CriminalIntent
+ Enter a title for the crime.
+ Title
+ Details
+ Solved
+
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/values/styles.xml b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Book Files/11_ViewPager/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java b/Book Files/11_ViewPager/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java
new file mode 100644
index 0000000..e0e2773
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.criminalintent;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/11_ViewPager/CriminalIntent/build.gradle b/Book Files/11_ViewPager/CriminalIntent/build.gradle
new file mode 100644
index 0000000..1ea4bd0
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Book Files/11_ViewPager/CriminalIntent/gradle.properties b/Book Files/11_ViewPager/CriminalIntent/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/Book Files/11_ViewPager/CriminalIntent/gradle/wrapper/gradle-wrapper.jar b/Book Files/11_ViewPager/CriminalIntent/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/Book Files/11_ViewPager/CriminalIntent/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/11_ViewPager/CriminalIntent/gradle/wrapper/gradle-wrapper.properties b/Book Files/11_ViewPager/CriminalIntent/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a82392d
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/11_ViewPager/CriminalIntent/gradlew b/Book Files/11_ViewPager/CriminalIntent/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/11_ViewPager/CriminalIntent/gradlew.bat b/Book Files/11_ViewPager/CriminalIntent/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/11_ViewPager/CriminalIntent/settings.gradle b/Book Files/11_ViewPager/CriminalIntent/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/11_ViewPager/CriminalIntent/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/11_ViewPager/LICENSE.txt b/Book Files/11_ViewPager/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Book Files/11_ViewPager/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Book Files/12_DateDialog/.DS_Store b/Book Files/12_DateDialog/.DS_Store
new file mode 100644
index 0000000..5008ddf
Binary files /dev/null and b/Book Files/12_DateDialog/.DS_Store differ
diff --git a/Book Files/12_DateDialog/CriminalIntent/.gitignore b/Book Files/12_DateDialog/CriminalIntent/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Book Files/12_DateDialog/CriminalIntent/.idea/compiler.xml b/Book Files/12_DateDialog/CriminalIntent/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/12_DateDialog/CriminalIntent/.idea/copyright/profiles_settings.xml b/Book Files/12_DateDialog/CriminalIntent/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Book Files/12_DateDialog/CriminalIntent/.idea/encodings.xml b/Book Files/12_DateDialog/CriminalIntent/.idea/encodings.xml
new file mode 100644
index 0000000..97626ba
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/12_DateDialog/CriminalIntent/.idea/gradle.xml b/Book Files/12_DateDialog/CriminalIntent/.idea/gradle.xml
new file mode 100644
index 0000000..7ac24c7
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/.idea/gradle.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/12_DateDialog/CriminalIntent/.idea/misc.xml b/Book Files/12_DateDialog/CriminalIntent/.idea/misc.xml
new file mode 100644
index 0000000..5d19981
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/.idea/misc.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/12_DateDialog/CriminalIntent/.idea/modules.xml b/Book Files/12_DateDialog/CriminalIntent/.idea/modules.xml
new file mode 100644
index 0000000..37be0dd
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/12_DateDialog/CriminalIntent/.idea/runConfigurations.xml b/Book Files/12_DateDialog/CriminalIntent/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/.gitignore b/Book Files/12_DateDialog/CriminalIntent/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/build.gradle b/Book Files/12_DateDialog/CriminalIntent/app/build.gradle
new file mode 100644
index 0000000..d05c462
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/app/build.gradle
@@ -0,0 +1,32 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ applicationId "com.bignerdranch.android.criminalintent"
+ minSdkVersion 19
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ testCompile 'junit:junit:4.12'
+ compile 'com.android.support:support-v4:25.3.0'
+ compile 'com.android.support:recyclerview-v7:25.3.0'
+ compile 'com.android.support.constraint:constraint-layout:1.0.2'
+}
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/proguard-rules.pro b/Book Files/12_DateDialog/CriminalIntent/app/proguard-rules.pro
new file mode 100644
index 0000000..99da964
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/chris/AndroidDeveloper/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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 *;
+#}
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java b/Book Files/12_DateDialog/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..3ed35e7
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.criminalintent;
+
+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.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.bignerdranch.android.criminalintent", appContext.getPackageName());
+ }
+}
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/AndroidManifest.xml b/Book Files/12_DateDialog/CriminalIntent/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..74975a6
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/app/src/main/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java b/Book Files/12_DateDialog/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java
new file mode 100644
index 0000000..cfa8b3d
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java
@@ -0,0 +1,45 @@
+package com.bignerdranch.android.criminalintent;
+
+import java.util.Date;
+import java.util.UUID;
+
+public class Crime {
+
+ private UUID mId;
+ private String mTitle;
+ private Date mDate;
+ private boolean mSolved;
+
+ public Crime() {
+ mId = UUID.randomUUID();
+ mDate = new Date();
+ }
+
+ public UUID getId() {
+ return mId;
+ }
+
+ public String getTitle() {
+ return mTitle;
+ }
+
+ public void setTitle(String title) {
+ mTitle = title;
+ }
+
+ public Date getDate() {
+ return mDate;
+ }
+
+ public void setDate(Date date) {
+ mDate = date;
+ }
+
+ public boolean isSolved() {
+ return mSolved;
+ }
+
+ public void setSolved(boolean solved) {
+ mSolved = solved;
+ }
+}
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java b/Book Files/12_DateDialog/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java
new file mode 100644
index 0000000..2613a81
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java
@@ -0,0 +1,118 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+
+import java.util.Date;
+import java.util.UUID;
+
+import static android.widget.CompoundButton.*;
+
+public class CrimeFragment extends Fragment {
+
+ private static final String ARG_CRIME_ID = "crime_id";
+ private static final String DIALOG_DATE = "DialogDate";
+
+ private static final int REQUEST_DATE = 0;
+
+ private Crime mCrime;
+ private EditText mTitleField;
+ private Button mDateButton;
+ private CheckBox mSolvedCheckbox;
+
+ public static CrimeFragment newInstance(UUID crimeId) {
+ Bundle args = new Bundle();
+ args.putSerializable(ARG_CRIME_ID, crimeId);
+
+ CrimeFragment fragment = new CrimeFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ UUID crimeId = (UUID) getArguments().getSerializable(ARG_CRIME_ID);
+ mCrime = CrimeLab.get(getActivity()).getCrime(crimeId);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.fragment_crime, container, false);
+
+ mTitleField = (EditText) v.findViewById(R.id.crime_title);
+ mTitleField.setText(mCrime.getTitle());
+ mTitleField.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ mCrime.setTitle(s.toString());
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+
+ }
+ });
+
+ mDateButton = (Button) v.findViewById(R.id.crime_date);
+ updateDate();
+ mDateButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ FragmentManager manager = getFragmentManager();
+ DatePickerFragment dialog = DatePickerFragment
+ .newInstance(mCrime.getDate());
+ dialog.setTargetFragment(CrimeFragment.this, REQUEST_DATE);
+ dialog.show(manager, DIALOG_DATE);
+ }
+ });
+
+ mSolvedCheckbox = (CheckBox) v.findViewById(R.id.crime_solved);
+ mSolvedCheckbox.setChecked(mCrime.isSolved());
+ mSolvedCheckbox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView,
+ boolean isChecked) {
+ mCrime.setSolved(isChecked);
+ }
+ });
+
+ return v;
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode != Activity.RESULT_OK) {
+ return;
+ }
+
+ if (requestCode == REQUEST_DATE) {
+ Date date = (Date) data
+ .getSerializableExtra(DatePickerFragment.EXTRA_DATE);
+ mCrime.setDate(date);
+ updateDate();
+ }
+ }
+
+ private void updateDate() {
+ mDateButton.setText(mCrime.getDate().toString());
+ }
+}
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java b/Book Files/12_DateDialog/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java
new file mode 100644
index 0000000..1679b63
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java
@@ -0,0 +1,45 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.content.Context;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+public class CrimeLab {
+ private static CrimeLab sCrimeLab;
+
+ private List mCrimes;
+
+ public static CrimeLab get(Context context) {
+ if (sCrimeLab == null) {
+ sCrimeLab = new CrimeLab(context);
+ }
+
+ return sCrimeLab;
+ }
+
+ private CrimeLab(Context context) {
+ mCrimes = new ArrayList<>();
+ for (int i = 0; i < 100; i++) {
+ Crime crime = new Crime();
+ crime.setTitle("Crime #" + i);
+ crime.setSolved(i % 2 == 0);
+ mCrimes.add(crime);
+ }
+ }
+
+ public List getCrimes() {
+ return mCrimes;
+ }
+
+ public Crime getCrime(UUID id) {
+ for (Crime crime : mCrimes) {
+ if (crime.getId().equals(id)) {
+ return crime;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java b/Book Files/12_DateDialog/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java
new file mode 100644
index 0000000..25557d2
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java
@@ -0,0 +1,11 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.support.v4.app.Fragment;
+
+public class CrimeListActivity extends SingleFragmentActivity {
+
+ @Override
+ protected Fragment createFragment() {
+ return new CrimeListFragment();
+ }
+}
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java b/Book Files/12_DateDialog/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java
new file mode 100644
index 0000000..43c5601
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java
@@ -0,0 +1,109 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.List;
+
+public class CrimeListFragment extends Fragment {
+ private RecyclerView mCrimeRecyclerView;
+ private CrimeAdapter mAdapter;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_crime_list, container, false);
+
+ mCrimeRecyclerView = (RecyclerView) view
+ .findViewById(R.id.crime_recycler_view);
+ mCrimeRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
+
+ updateUI();
+
+ return view;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ updateUI();
+ }
+
+ private void updateUI() {
+ CrimeLab crimeLab = CrimeLab.get(getActivity());
+ List crimes = crimeLab.getCrimes();
+
+ if (mAdapter == null) {
+ mAdapter = new CrimeAdapter(crimes);
+ mCrimeRecyclerView.setAdapter(mAdapter);
+ } else {
+ mAdapter.notifyDataSetChanged();
+ }
+ }
+
+ private class CrimeHolder extends RecyclerView.ViewHolder
+ implements View.OnClickListener {
+
+ private Crime mCrime;
+
+ private TextView mTitleTextView;
+ private TextView mDateTextView;
+ private ImageView mSolvedImageView;
+
+ public CrimeHolder(LayoutInflater inflater, ViewGroup parent) {
+ super(inflater.inflate(R.layout.list_item_crime, parent, false));
+ itemView.setOnClickListener(this);
+
+ mTitleTextView = (TextView) itemView.findViewById(R.id.crime_title);
+ mDateTextView = (TextView) itemView.findViewById(R.id.crime_date);
+ mSolvedImageView = (ImageView) itemView.findViewById(R.id.crime_solved);
+ }
+
+ public void bind(Crime crime) {
+ mCrime = crime;
+ mTitleTextView.setText(mCrime.getTitle());
+ mDateTextView.setText(mCrime.getDate().toString());
+ mSolvedImageView.setVisibility(crime.isSolved() ? View.VISIBLE : View.GONE);
+ }
+
+ @Override
+ public void onClick(View view) {
+ Intent intent = CrimePagerActivity.newIntent(getActivity(), mCrime.getId());
+ startActivity(intent);
+ }
+ }
+
+ private class CrimeAdapter extends RecyclerView.Adapter {
+
+ private List mCrimes;
+
+ public CrimeAdapter(List crimes) {
+ mCrimes = crimes;
+ }
+
+ @Override
+ public CrimeHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
+ return new CrimeHolder(layoutInflater, parent);
+ }
+
+ @Override
+ public void onBindViewHolder(CrimeHolder holder, int position) {
+ Crime crime = mCrimes.get(position);
+ holder.bind(crime);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mCrimes.size();
+ }
+ }
+}
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimePagerActivity.java b/Book Files/12_DateDialog/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimePagerActivity.java
new file mode 100644
index 0000000..b11cca9
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimePagerActivity.java
@@ -0,0 +1,62 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentStatePagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.v7.app.AppCompatActivity;
+
+import java.util.List;
+import java.util.UUID;
+
+public class CrimePagerActivity extends AppCompatActivity {
+
+ private static final String EXTRA_CRIME_ID =
+ "com.bignerdranch.android.criminalintent.crime_id";
+
+ private ViewPager mViewPager;
+ private List mCrimes;
+
+ public static Intent newIntent(Context packageContext, UUID crimeId) {
+ Intent intent = new Intent(packageContext, CrimePagerActivity.class);
+ intent.putExtra(EXTRA_CRIME_ID, crimeId);
+ return intent;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_crime_pager);
+
+ UUID crimeId = (UUID) getIntent()
+ .getSerializableExtra(EXTRA_CRIME_ID);
+
+ mViewPager = (ViewPager) findViewById(R.id.crime_view_pager);
+
+ mCrimes = CrimeLab.get(this).getCrimes();
+ FragmentManager fragmentManager = getSupportFragmentManager();
+ mViewPager.setAdapter(new FragmentStatePagerAdapter(fragmentManager) {
+
+ @Override
+ public Fragment getItem(int position) {
+ Crime crime = mCrimes.get(position);
+ return CrimeFragment.newInstance(crime.getId());
+ }
+
+ @Override
+ public int getCount() {
+ return mCrimes.size();
+ }
+ });
+
+ for (int i = 0; i < mCrimes.size(); i++) {
+ if (mCrimes.get(i).getId().equals(crimeId)) {
+ mViewPager.setCurrentItem(i);
+ break;
+ }
+ }
+ }
+}
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/DatePickerFragment.java b/Book Files/12_DateDialog/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/DatePickerFragment.java
new file mode 100644
index 0000000..51968c5
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/DatePickerFragment.java
@@ -0,0 +1,80 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+import android.support.v7.app.AlertDialog;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.DatePicker;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+public class DatePickerFragment extends DialogFragment {
+
+ public static final String EXTRA_DATE =
+ "com.bignerdranch.android.criminalintent.date";
+
+ private static final String ARG_DATE = "date";
+
+ private DatePicker mDatePicker;
+
+ public static DatePickerFragment newInstance(Date date) {
+ Bundle args = new Bundle();
+ args.putSerializable(ARG_DATE, date);
+
+ DatePickerFragment fragment = new DatePickerFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ Date date = (Date) getArguments().getSerializable(ARG_DATE);
+
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(date);
+ int year = calendar.get(Calendar.YEAR);
+ int month = calendar.get(Calendar.MONTH);
+ int day = calendar.get(Calendar.DAY_OF_MONTH);
+
+ View v = LayoutInflater.from(getActivity())
+ .inflate(R.layout.dialog_date, null);
+
+ mDatePicker = (DatePicker) v.findViewById(R.id.dialog_date_picker);
+ mDatePicker.init(year, month, day, null);
+
+ return new AlertDialog.Builder(getActivity())
+ .setView(v)
+ .setTitle(R.string.date_picker_title)
+ .setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ int year = mDatePicker.getYear();
+ int month = mDatePicker.getMonth();
+ int day = mDatePicker.getDayOfMonth();
+ Date date = new GregorianCalendar(year, month, day).getTime();
+ sendResult(Activity.RESULT_OK, date);
+ }
+ })
+ .create();
+ }
+
+ private void sendResult(int resultCode, Date date) {
+ if (getTargetFragment() == null) {
+ return;
+ }
+
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_DATE, date);
+
+ getTargetFragment()
+ .onActivityResult(getTargetRequestCode(), resultCode, intent);
+ }
+}
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java b/Book Files/12_DateDialog/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java
new file mode 100644
index 0000000..9e1d2d8
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java
@@ -0,0 +1,27 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.AppCompatActivity;
+
+public abstract class SingleFragmentActivity extends AppCompatActivity {
+
+ protected abstract Fragment createFragment();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_fragment);
+
+ FragmentManager fm = getSupportFragmentManager();
+ Fragment fragment = fm.findFragmentById(R.id.fragment_container);
+
+ if (fragment == null) {
+ fragment = createFragment();
+ fm.beginTransaction()
+ .add(R.id.fragment_container, fragment)
+ .commit();
+ }
+ }
+}
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/drawable-hdpi/ic_solved.png b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/drawable-hdpi/ic_solved.png
new file mode 100644
index 0000000..e9b9263
Binary files /dev/null and b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/drawable-hdpi/ic_solved.png differ
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/drawable-mdpi/ic_solved.png b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/drawable-mdpi/ic_solved.png
new file mode 100644
index 0000000..9548d3c
Binary files /dev/null and b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/drawable-mdpi/ic_solved.png differ
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_solved.png b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_solved.png
new file mode 100644
index 0000000..6ab438d
Binary files /dev/null and b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_solved.png differ
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_solved.png b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_solved.png
new file mode 100644
index 0000000..ca05191
Binary files /dev/null and b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_solved.png differ
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/drawable-xxxhdpi/ic_solved.png b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/drawable-xxxhdpi/ic_solved.png
new file mode 100644
index 0000000..15d3cf4
Binary files /dev/null and b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/drawable-xxxhdpi/ic_solved.png differ
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/layout/activity_crime_pager.xml b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/layout/activity_crime_pager.xml
new file mode 100644
index 0000000..a80c096
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/layout/activity_crime_pager.xml
@@ -0,0 +1,5 @@
+
+
\ No newline at end of file
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/layout/activity_fragment.xml b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/layout/activity_fragment.xml
new file mode 100644
index 0000000..1773143
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/layout/activity_fragment.xml
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/layout/dialog_date.xml b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/layout/dialog_date.xml
new file mode 100644
index 0000000..c35ec4c
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/layout/dialog_date.xml
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/layout/fragment_crime.xml b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/layout/fragment_crime.xml
new file mode 100644
index 0000000..20734cf
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/layout/fragment_crime.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml
new file mode 100644
index 0000000..4d23b7c
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml
@@ -0,0 +1,5 @@
+
+
\ No newline at end of file
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/layout/list_item_crime.xml b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/layout/list_item_crime.xml
new file mode 100644
index 0000000..e149593
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/layout/list_item_crime.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/values/colors.xml b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/values/dimens.xml b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/values/strings.xml b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..463bc29
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/values/strings.xml
@@ -0,0 +1,8 @@
+
+ CriminalIntent
+ Enter a title for the crime.
+ Title
+ Details
+ Solved
+ Date of crime:
+
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/values/styles.xml b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Book Files/12_DateDialog/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java b/Book Files/12_DateDialog/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java
new file mode 100644
index 0000000..e0e2773
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.criminalintent;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/12_DateDialog/CriminalIntent/build.gradle b/Book Files/12_DateDialog/CriminalIntent/build.gradle
new file mode 100644
index 0000000..1ea4bd0
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Book Files/12_DateDialog/CriminalIntent/gradle.properties b/Book Files/12_DateDialog/CriminalIntent/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/Book Files/12_DateDialog/CriminalIntent/gradle/wrapper/gradle-wrapper.jar b/Book Files/12_DateDialog/CriminalIntent/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/Book Files/12_DateDialog/CriminalIntent/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/12_DateDialog/CriminalIntent/gradle/wrapper/gradle-wrapper.properties b/Book Files/12_DateDialog/CriminalIntent/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a82392d
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/12_DateDialog/CriminalIntent/gradlew b/Book Files/12_DateDialog/CriminalIntent/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/12_DateDialog/CriminalIntent/gradlew.bat b/Book Files/12_DateDialog/CriminalIntent/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/12_DateDialog/CriminalIntent/settings.gradle b/Book Files/12_DateDialog/CriminalIntent/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/12_DateDialog/CriminalIntent/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/12_DateDialog/LICENSE.txt b/Book Files/12_DateDialog/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Book Files/12_DateDialog/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Book Files/13_Toolbar/.DS_Store b/Book Files/13_Toolbar/.DS_Store
new file mode 100644
index 0000000..e742a37
Binary files /dev/null and b/Book Files/13_Toolbar/.DS_Store differ
diff --git a/Book Files/13_Toolbar/CriminalIntent/.gitignore b/Book Files/13_Toolbar/CriminalIntent/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Book Files/13_Toolbar/CriminalIntent/.idea/compiler.xml b/Book Files/13_Toolbar/CriminalIntent/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/13_Toolbar/CriminalIntent/.idea/copyright/profiles_settings.xml b/Book Files/13_Toolbar/CriminalIntent/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Book Files/13_Toolbar/CriminalIntent/.idea/encodings.xml b/Book Files/13_Toolbar/CriminalIntent/.idea/encodings.xml
new file mode 100644
index 0000000..97626ba
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/13_Toolbar/CriminalIntent/.idea/gradle.xml b/Book Files/13_Toolbar/CriminalIntent/.idea/gradle.xml
new file mode 100644
index 0000000..7ac24c7
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/.idea/gradle.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/13_Toolbar/CriminalIntent/.idea/misc.xml b/Book Files/13_Toolbar/CriminalIntent/.idea/misc.xml
new file mode 100644
index 0000000..5d19981
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/.idea/misc.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/13_Toolbar/CriminalIntent/.idea/modules.xml b/Book Files/13_Toolbar/CriminalIntent/.idea/modules.xml
new file mode 100644
index 0000000..37be0dd
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/13_Toolbar/CriminalIntent/.idea/runConfigurations.xml b/Book Files/13_Toolbar/CriminalIntent/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/.gitignore b/Book Files/13_Toolbar/CriminalIntent/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/build.gradle b/Book Files/13_Toolbar/CriminalIntent/app/build.gradle
new file mode 100644
index 0000000..d05c462
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/app/build.gradle
@@ -0,0 +1,32 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ applicationId "com.bignerdranch.android.criminalintent"
+ minSdkVersion 19
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ testCompile 'junit:junit:4.12'
+ compile 'com.android.support:support-v4:25.3.0'
+ compile 'com.android.support:recyclerview-v7:25.3.0'
+ compile 'com.android.support.constraint:constraint-layout:1.0.2'
+}
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/proguard-rules.pro b/Book Files/13_Toolbar/CriminalIntent/app/proguard-rules.pro
new file mode 100644
index 0000000..99da964
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/chris/AndroidDeveloper/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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 *;
+#}
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java b/Book Files/13_Toolbar/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..3ed35e7
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.criminalintent;
+
+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.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.bignerdranch.android.criminalintent", appContext.getPackageName());
+ }
+}
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/AndroidManifest.xml b/Book Files/13_Toolbar/CriminalIntent/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..215be2b
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/app/src/main/AndroidManifest.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java b/Book Files/13_Toolbar/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java
new file mode 100644
index 0000000..cfa8b3d
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java
@@ -0,0 +1,45 @@
+package com.bignerdranch.android.criminalintent;
+
+import java.util.Date;
+import java.util.UUID;
+
+public class Crime {
+
+ private UUID mId;
+ private String mTitle;
+ private Date mDate;
+ private boolean mSolved;
+
+ public Crime() {
+ mId = UUID.randomUUID();
+ mDate = new Date();
+ }
+
+ public UUID getId() {
+ return mId;
+ }
+
+ public String getTitle() {
+ return mTitle;
+ }
+
+ public void setTitle(String title) {
+ mTitle = title;
+ }
+
+ public Date getDate() {
+ return mDate;
+ }
+
+ public void setDate(Date date) {
+ mDate = date;
+ }
+
+ public boolean isSolved() {
+ return mSolved;
+ }
+
+ public void setSolved(boolean solved) {
+ mSolved = solved;
+ }
+}
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java b/Book Files/13_Toolbar/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java
new file mode 100644
index 0000000..2613a81
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java
@@ -0,0 +1,118 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+
+import java.util.Date;
+import java.util.UUID;
+
+import static android.widget.CompoundButton.*;
+
+public class CrimeFragment extends Fragment {
+
+ private static final String ARG_CRIME_ID = "crime_id";
+ private static final String DIALOG_DATE = "DialogDate";
+
+ private static final int REQUEST_DATE = 0;
+
+ private Crime mCrime;
+ private EditText mTitleField;
+ private Button mDateButton;
+ private CheckBox mSolvedCheckbox;
+
+ public static CrimeFragment newInstance(UUID crimeId) {
+ Bundle args = new Bundle();
+ args.putSerializable(ARG_CRIME_ID, crimeId);
+
+ CrimeFragment fragment = new CrimeFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ UUID crimeId = (UUID) getArguments().getSerializable(ARG_CRIME_ID);
+ mCrime = CrimeLab.get(getActivity()).getCrime(crimeId);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.fragment_crime, container, false);
+
+ mTitleField = (EditText) v.findViewById(R.id.crime_title);
+ mTitleField.setText(mCrime.getTitle());
+ mTitleField.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ mCrime.setTitle(s.toString());
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+
+ }
+ });
+
+ mDateButton = (Button) v.findViewById(R.id.crime_date);
+ updateDate();
+ mDateButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ FragmentManager manager = getFragmentManager();
+ DatePickerFragment dialog = DatePickerFragment
+ .newInstance(mCrime.getDate());
+ dialog.setTargetFragment(CrimeFragment.this, REQUEST_DATE);
+ dialog.show(manager, DIALOG_DATE);
+ }
+ });
+
+ mSolvedCheckbox = (CheckBox) v.findViewById(R.id.crime_solved);
+ mSolvedCheckbox.setChecked(mCrime.isSolved());
+ mSolvedCheckbox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView,
+ boolean isChecked) {
+ mCrime.setSolved(isChecked);
+ }
+ });
+
+ return v;
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode != Activity.RESULT_OK) {
+ return;
+ }
+
+ if (requestCode == REQUEST_DATE) {
+ Date date = (Date) data
+ .getSerializableExtra(DatePickerFragment.EXTRA_DATE);
+ mCrime.setDate(date);
+ updateDate();
+ }
+ }
+
+ private void updateDate() {
+ mDateButton.setText(mCrime.getDate().toString());
+ }
+}
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java b/Book Files/13_Toolbar/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java
new file mode 100644
index 0000000..17590e0
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java
@@ -0,0 +1,43 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.content.Context;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+public class CrimeLab {
+ private static CrimeLab sCrimeLab;
+
+ private List mCrimes;
+
+ public static CrimeLab get(Context context) {
+ if (sCrimeLab == null) {
+ sCrimeLab = new CrimeLab(context);
+ }
+
+ return sCrimeLab;
+ }
+
+ private CrimeLab(Context context) {
+ mCrimes = new ArrayList<>();
+ }
+
+ public void addCrime(Crime c) {
+ mCrimes.add(c);
+ }
+
+ public List getCrimes() {
+ return mCrimes;
+ }
+
+ public Crime getCrime(UUID id) {
+ for (Crime crime : mCrimes) {
+ if (crime.getId().equals(id)) {
+ return crime;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java b/Book Files/13_Toolbar/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java
new file mode 100644
index 0000000..25557d2
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java
@@ -0,0 +1,11 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.support.v4.app.Fragment;
+
+public class CrimeListActivity extends SingleFragmentActivity {
+
+ @Override
+ protected Fragment createFragment() {
+ return new CrimeListFragment();
+ }
+}
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java b/Book Files/13_Toolbar/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java
new file mode 100644
index 0000000..b4d9532
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java
@@ -0,0 +1,181 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.List;
+
+public class CrimeListFragment extends Fragment {
+
+ private static final String SAVED_SUBTITLE_VISIBLE = "subtitle";
+
+ private RecyclerView mCrimeRecyclerView;
+ private CrimeAdapter mAdapter;
+ private boolean mSubtitleVisible;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_crime_list, container, false);
+
+ mCrimeRecyclerView = (RecyclerView) view
+ .findViewById(R.id.crime_recycler_view);
+ mCrimeRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
+
+ if (savedInstanceState != null) {
+ mSubtitleVisible = savedInstanceState.getBoolean(SAVED_SUBTITLE_VISIBLE);
+ }
+
+ updateUI();
+
+ return view;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ updateUI();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(SAVED_SUBTITLE_VISIBLE, mSubtitleVisible);
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ inflater.inflate(R.menu.fragment_crime_list, menu);
+
+ MenuItem subtitleItem = menu.findItem(R.id.show_subtitle);
+ if (mSubtitleVisible) {
+ subtitleItem.setTitle(R.string.hide_subtitle);
+ } else {
+ subtitleItem.setTitle(R.string.show_subtitle);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.new_crime:
+ Crime crime = new Crime();
+ CrimeLab.get(getActivity()).addCrime(crime);
+ Intent intent = CrimePagerActivity
+ .newIntent(getActivity(), crime.getId());
+ startActivity(intent);
+ return true;
+ case R.id.show_subtitle:
+ mSubtitleVisible = !mSubtitleVisible;
+ getActivity().invalidateOptionsMenu();
+ updateSubtitle();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private void updateSubtitle() {
+ CrimeLab crimeLab = CrimeLab.get(getActivity());
+ int crimeCount = crimeLab.getCrimes().size();
+ String subtitle = getString(R.string.subtitle_format, crimeCount);
+
+ if (!mSubtitleVisible) {
+ subtitle = null;
+ }
+
+ AppCompatActivity activity = (AppCompatActivity) getActivity();
+ activity.getSupportActionBar().setSubtitle(subtitle);
+ }
+
+ private void updateUI() {
+ CrimeLab crimeLab = CrimeLab.get(getActivity());
+ List crimes = crimeLab.getCrimes();
+
+ if (mAdapter == null) {
+ mAdapter = new CrimeAdapter(crimes);
+ mCrimeRecyclerView.setAdapter(mAdapter);
+ } else {
+ mAdapter.notifyDataSetChanged();
+ }
+
+ updateSubtitle();
+ }
+
+ private class CrimeHolder extends RecyclerView.ViewHolder
+ implements View.OnClickListener {
+
+ private Crime mCrime;
+
+ private TextView mTitleTextView;
+ private TextView mDateTextView;
+ private ImageView mSolvedImageView;
+
+ public CrimeHolder(LayoutInflater inflater, ViewGroup parent) {
+ super(inflater.inflate(R.layout.list_item_crime, parent, false));
+ itemView.setOnClickListener(this);
+
+ mTitleTextView = (TextView) itemView.findViewById(R.id.crime_title);
+ mDateTextView = (TextView) itemView.findViewById(R.id.crime_date);
+ mSolvedImageView = (ImageView) itemView.findViewById(R.id.crime_solved);
+ }
+
+ public void bind(Crime crime) {
+ mCrime = crime;
+ mTitleTextView.setText(mCrime.getTitle());
+ mDateTextView.setText(mCrime.getDate().toString());
+ mSolvedImageView.setVisibility(crime.isSolved() ? View.VISIBLE : View.GONE);
+ }
+
+ @Override
+ public void onClick(View view) {
+ Intent intent = CrimePagerActivity.newIntent(getActivity(), mCrime.getId());
+ startActivity(intent);
+ }
+ }
+
+ private class CrimeAdapter extends RecyclerView.Adapter {
+
+ private List mCrimes;
+
+ public CrimeAdapter(List crimes) {
+ mCrimes = crimes;
+ }
+
+ @Override
+ public CrimeHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
+ return new CrimeHolder(layoutInflater, parent);
+ }
+
+ @Override
+ public void onBindViewHolder(CrimeHolder holder, int position) {
+ Crime crime = mCrimes.get(position);
+ holder.bind(crime);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mCrimes.size();
+ }
+ }
+}
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimePagerActivity.java b/Book Files/13_Toolbar/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimePagerActivity.java
new file mode 100644
index 0000000..b11cca9
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimePagerActivity.java
@@ -0,0 +1,62 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentStatePagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.v7.app.AppCompatActivity;
+
+import java.util.List;
+import java.util.UUID;
+
+public class CrimePagerActivity extends AppCompatActivity {
+
+ private static final String EXTRA_CRIME_ID =
+ "com.bignerdranch.android.criminalintent.crime_id";
+
+ private ViewPager mViewPager;
+ private List mCrimes;
+
+ public static Intent newIntent(Context packageContext, UUID crimeId) {
+ Intent intent = new Intent(packageContext, CrimePagerActivity.class);
+ intent.putExtra(EXTRA_CRIME_ID, crimeId);
+ return intent;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_crime_pager);
+
+ UUID crimeId = (UUID) getIntent()
+ .getSerializableExtra(EXTRA_CRIME_ID);
+
+ mViewPager = (ViewPager) findViewById(R.id.crime_view_pager);
+
+ mCrimes = CrimeLab.get(this).getCrimes();
+ FragmentManager fragmentManager = getSupportFragmentManager();
+ mViewPager.setAdapter(new FragmentStatePagerAdapter(fragmentManager) {
+
+ @Override
+ public Fragment getItem(int position) {
+ Crime crime = mCrimes.get(position);
+ return CrimeFragment.newInstance(crime.getId());
+ }
+
+ @Override
+ public int getCount() {
+ return mCrimes.size();
+ }
+ });
+
+ for (int i = 0; i < mCrimes.size(); i++) {
+ if (mCrimes.get(i).getId().equals(crimeId)) {
+ mViewPager.setCurrentItem(i);
+ break;
+ }
+ }
+ }
+}
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/DatePickerFragment.java b/Book Files/13_Toolbar/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/DatePickerFragment.java
new file mode 100644
index 0000000..51968c5
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/DatePickerFragment.java
@@ -0,0 +1,80 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+import android.support.v7.app.AlertDialog;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.DatePicker;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+public class DatePickerFragment extends DialogFragment {
+
+ public static final String EXTRA_DATE =
+ "com.bignerdranch.android.criminalintent.date";
+
+ private static final String ARG_DATE = "date";
+
+ private DatePicker mDatePicker;
+
+ public static DatePickerFragment newInstance(Date date) {
+ Bundle args = new Bundle();
+ args.putSerializable(ARG_DATE, date);
+
+ DatePickerFragment fragment = new DatePickerFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ Date date = (Date) getArguments().getSerializable(ARG_DATE);
+
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(date);
+ int year = calendar.get(Calendar.YEAR);
+ int month = calendar.get(Calendar.MONTH);
+ int day = calendar.get(Calendar.DAY_OF_MONTH);
+
+ View v = LayoutInflater.from(getActivity())
+ .inflate(R.layout.dialog_date, null);
+
+ mDatePicker = (DatePicker) v.findViewById(R.id.dialog_date_picker);
+ mDatePicker.init(year, month, day, null);
+
+ return new AlertDialog.Builder(getActivity())
+ .setView(v)
+ .setTitle(R.string.date_picker_title)
+ .setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ int year = mDatePicker.getYear();
+ int month = mDatePicker.getMonth();
+ int day = mDatePicker.getDayOfMonth();
+ Date date = new GregorianCalendar(year, month, day).getTime();
+ sendResult(Activity.RESULT_OK, date);
+ }
+ })
+ .create();
+ }
+
+ private void sendResult(int resultCode, Date date) {
+ if (getTargetFragment() == null) {
+ return;
+ }
+
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_DATE, date);
+
+ getTargetFragment()
+ .onActivityResult(getTargetRequestCode(), resultCode, intent);
+ }
+}
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java b/Book Files/13_Toolbar/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java
new file mode 100644
index 0000000..9e1d2d8
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java
@@ -0,0 +1,27 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.AppCompatActivity;
+
+public abstract class SingleFragmentActivity extends AppCompatActivity {
+
+ protected abstract Fragment createFragment();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_fragment);
+
+ FragmentManager fm = getSupportFragmentManager();
+ Fragment fragment = fm.findFragmentById(R.id.fragment_container);
+
+ if (fragment == null) {
+ fragment = createFragment();
+ fm.beginTransaction()
+ .add(R.id.fragment_container, fragment)
+ .commit();
+ }
+ }
+}
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/drawable-hdpi/ic_menu_add.png b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/drawable-hdpi/ic_menu_add.png
new file mode 100644
index 0000000..1ae5b2d
Binary files /dev/null and b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/drawable-hdpi/ic_menu_add.png differ
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/drawable-hdpi/ic_solved.png b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/drawable-hdpi/ic_solved.png
new file mode 100644
index 0000000..e9b9263
Binary files /dev/null and b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/drawable-hdpi/ic_solved.png differ
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/drawable-mdpi/ic_menu_add.png b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/drawable-mdpi/ic_menu_add.png
new file mode 100644
index 0000000..d51f0dd
Binary files /dev/null and b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/drawable-mdpi/ic_menu_add.png differ
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/drawable-mdpi/ic_solved.png b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/drawable-mdpi/ic_solved.png
new file mode 100644
index 0000000..9548d3c
Binary files /dev/null and b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/drawable-mdpi/ic_solved.png differ
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_menu_add.png b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_menu_add.png
new file mode 100644
index 0000000..9ea0eeb
Binary files /dev/null and b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_menu_add.png differ
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_solved.png b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_solved.png
new file mode 100644
index 0000000..6ab438d
Binary files /dev/null and b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_solved.png differ
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_menu_add.png b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_menu_add.png
new file mode 100644
index 0000000..75f192a
Binary files /dev/null and b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_menu_add.png differ
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_solved.png b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_solved.png
new file mode 100644
index 0000000..ca05191
Binary files /dev/null and b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_solved.png differ
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/drawable-xxxhdpi/ic_solved.png b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/drawable-xxxhdpi/ic_solved.png
new file mode 100644
index 0000000..15d3cf4
Binary files /dev/null and b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/drawable-xxxhdpi/ic_solved.png differ
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/layout/activity_crime_pager.xml b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/layout/activity_crime_pager.xml
new file mode 100644
index 0000000..a80c096
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/layout/activity_crime_pager.xml
@@ -0,0 +1,5 @@
+
+
\ No newline at end of file
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/layout/activity_fragment.xml b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/layout/activity_fragment.xml
new file mode 100644
index 0000000..1773143
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/layout/activity_fragment.xml
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/layout/dialog_date.xml b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/layout/dialog_date.xml
new file mode 100644
index 0000000..c35ec4c
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/layout/dialog_date.xml
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/layout/fragment_crime.xml b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/layout/fragment_crime.xml
new file mode 100644
index 0000000..20734cf
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/layout/fragment_crime.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml
new file mode 100644
index 0000000..4d23b7c
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml
@@ -0,0 +1,5 @@
+
+
\ No newline at end of file
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/layout/list_item_crime.xml b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/layout/list_item_crime.xml
new file mode 100644
index 0000000..e149593
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/layout/list_item_crime.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/menu/fragment_crime_list.xml b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/menu/fragment_crime_list.xml
new file mode 100644
index 0000000..de478c1
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/menu/fragment_crime_list.xml
@@ -0,0 +1,14 @@
+
+
\ No newline at end of file
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/values/colors.xml b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/values/dimens.xml b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/values/strings.xml b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..2b440b4
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/values/strings.xml
@@ -0,0 +1,12 @@
+
+ CriminalIntent
+ Enter a title for the crime.
+ Title
+ Details
+ Solved
+ Date of crime:
+ New Crime
+ Show Subtitle
+ Hide Subtitle
+ %1$d crimes
+
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/values/styles.xml b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Book Files/13_Toolbar/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java b/Book Files/13_Toolbar/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java
new file mode 100644
index 0000000..e0e2773
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.criminalintent;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/13_Toolbar/CriminalIntent/build.gradle b/Book Files/13_Toolbar/CriminalIntent/build.gradle
new file mode 100644
index 0000000..1ea4bd0
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Book Files/13_Toolbar/CriminalIntent/gradle.properties b/Book Files/13_Toolbar/CriminalIntent/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/Book Files/13_Toolbar/CriminalIntent/gradle/wrapper/gradle-wrapper.jar b/Book Files/13_Toolbar/CriminalIntent/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/Book Files/13_Toolbar/CriminalIntent/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/13_Toolbar/CriminalIntent/gradle/wrapper/gradle-wrapper.properties b/Book Files/13_Toolbar/CriminalIntent/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a82392d
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/13_Toolbar/CriminalIntent/gradlew b/Book Files/13_Toolbar/CriminalIntent/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/13_Toolbar/CriminalIntent/gradlew.bat b/Book Files/13_Toolbar/CriminalIntent/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/13_Toolbar/CriminalIntent/settings.gradle b/Book Files/13_Toolbar/CriminalIntent/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/13_Toolbar/CriminalIntent/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/13_Toolbar/LICENSE.txt b/Book Files/13_Toolbar/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Book Files/13_Toolbar/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/.gitignore b/Book Files/14_LocalDatabases/CriminalIntent/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/.gitignore b/Book Files/14_LocalDatabases/CriminalIntent/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/build.gradle b/Book Files/14_LocalDatabases/CriminalIntent/app/build.gradle
new file mode 100644
index 0000000..d05c462
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/app/build.gradle
@@ -0,0 +1,32 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ applicationId "com.bignerdranch.android.criminalintent"
+ minSdkVersion 19
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ testCompile 'junit:junit:4.12'
+ compile 'com.android.support:support-v4:25.3.0'
+ compile 'com.android.support:recyclerview-v7:25.3.0'
+ compile 'com.android.support.constraint:constraint-layout:1.0.2'
+}
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/proguard-rules.pro b/Book Files/14_LocalDatabases/CriminalIntent/app/proguard-rules.pro
new file mode 100644
index 0000000..99da964
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/chris/AndroidDeveloper/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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 *;
+#}
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java b/Book Files/14_LocalDatabases/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..3ed35e7
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.criminalintent;
+
+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.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.bignerdranch.android.criminalintent", appContext.getPackageName());
+ }
+}
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/AndroidManifest.xml b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..215be2b
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/AndroidManifest.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java
new file mode 100644
index 0000000..dff9da5
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java
@@ -0,0 +1,49 @@
+package com.bignerdranch.android.criminalintent;
+
+import java.util.Date;
+import java.util.UUID;
+
+public class Crime {
+
+ private UUID mId;
+ private String mTitle;
+ private Date mDate;
+ private boolean mSolved;
+
+ public Crime() {
+ this(UUID.randomUUID());
+ }
+
+ public Crime(UUID id) {
+ mId = id;
+ mDate = new Date();
+ }
+
+ public UUID getId() {
+ return mId;
+ }
+
+ public String getTitle() {
+ return mTitle;
+ }
+
+ public void setTitle(String title) {
+ mTitle = title;
+ }
+
+ public Date getDate() {
+ return mDate;
+ }
+
+ public void setDate(Date date) {
+ mDate = date;
+ }
+
+ public boolean isSolved() {
+ return mSolved;
+ }
+
+ public void setSolved(boolean solved) {
+ mSolved = solved;
+ }
+}
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java
new file mode 100644
index 0000000..0b3e1d0
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java
@@ -0,0 +1,126 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+
+import java.util.Date;
+import java.util.UUID;
+
+import static android.widget.CompoundButton.*;
+
+public class CrimeFragment extends Fragment {
+
+ private static final String ARG_CRIME_ID = "crime_id";
+ private static final String DIALOG_DATE = "DialogDate";
+
+ private static final int REQUEST_DATE = 0;
+
+ private Crime mCrime;
+ private EditText mTitleField;
+ private Button mDateButton;
+ private CheckBox mSolvedCheckbox;
+
+ public static CrimeFragment newInstance(UUID crimeId) {
+ Bundle args = new Bundle();
+ args.putSerializable(ARG_CRIME_ID, crimeId);
+
+ CrimeFragment fragment = new CrimeFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ UUID crimeId = (UUID) getArguments().getSerializable(ARG_CRIME_ID);
+ mCrime = CrimeLab.get(getActivity()).getCrime(crimeId);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.fragment_crime, container, false);
+
+ mTitleField = (EditText) v.findViewById(R.id.crime_title);
+ mTitleField.setText(mCrime.getTitle());
+ mTitleField.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ mCrime.setTitle(s.toString());
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+
+ }
+ });
+
+ mDateButton = (Button) v.findViewById(R.id.crime_date);
+ updateDate();
+ mDateButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ FragmentManager manager = getFragmentManager();
+ DatePickerFragment dialog = DatePickerFragment
+ .newInstance(mCrime.getDate());
+ dialog.setTargetFragment(CrimeFragment.this, REQUEST_DATE);
+ dialog.show(manager, DIALOG_DATE);
+ }
+ });
+
+ mSolvedCheckbox = (CheckBox) v.findViewById(R.id.crime_solved);
+ mSolvedCheckbox.setChecked(mCrime.isSolved());
+ mSolvedCheckbox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView,
+ boolean isChecked) {
+ mCrime.setSolved(isChecked);
+ }
+ });
+
+ return v;
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ CrimeLab.get(getActivity())
+ .updateCrime(mCrime);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode != Activity.RESULT_OK) {
+ return;
+ }
+
+ if (requestCode == REQUEST_DATE) {
+ Date date = (Date) data
+ .getSerializableExtra(DatePickerFragment.EXTRA_DATE);
+ mCrime.setDate(date);
+ updateDate();
+ }
+ }
+
+ private void updateDate() {
+ mDateButton.setText(mCrime.getDate().toString());
+ }
+}
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java
new file mode 100644
index 0000000..351cd52
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java
@@ -0,0 +1,107 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+
+import com.bignerdranch.android.criminalintent.database.CrimeBaseHelper;
+import com.bignerdranch.android.criminalintent.database.CrimeCursorWrapper;
+import com.bignerdranch.android.criminalintent.database.CrimeDbSchema;
+import com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import static com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable.*;
+import static com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable.Cols.*;
+
+public class CrimeLab {
+ private static CrimeLab sCrimeLab;
+ private Context mContext;
+ private SQLiteDatabase mDatabase;
+
+ public static CrimeLab get(Context context) {
+ if (sCrimeLab == null) {
+ sCrimeLab = new CrimeLab(context);
+ }
+
+ return sCrimeLab;
+ }
+
+ private CrimeLab(Context context) {
+ mContext = context.getApplicationContext();
+ mDatabase = new CrimeBaseHelper(mContext)
+ .getWritableDatabase();
+
+ }
+
+ public void addCrime(Crime c) {
+ ContentValues values = getContentValues(c);
+ mDatabase.insert(CrimeTable.NAME, null, values);
+ }
+
+ public List getCrimes() {
+ List crimes = new ArrayList<>();
+ CrimeCursorWrapper cursor = queryCrimes(null, null);
+ try {
+ cursor.moveToFirst();
+ while (!cursor.isAfterLast()) {
+ crimes.add(cursor.getCrime());
+ cursor.moveToNext();
+ }
+ } finally {
+ cursor.close();
+ }
+ return crimes;
+ }
+
+ public Crime getCrime(UUID id) {
+ CrimeCursorWrapper cursor = queryCrimes(
+ CrimeTable.Cols.UUID + " = ?",
+ new String[]{id.toString()}
+ );
+ try {
+ if (cursor.getCount() == 0) {
+ return null;
+ }
+ cursor.moveToFirst();
+ return cursor.getCrime();
+ } finally {
+ cursor.close();
+ }
+ }
+
+ public void updateCrime(Crime crime) {
+ String uuidString = crime.getId().toString();
+ ContentValues values = getContentValues(crime);
+ mDatabase.update(CrimeTable.NAME, values,
+ CrimeTable.Cols.UUID + " = ?",
+ new String[]{uuidString});
+ }
+
+ private CrimeCursorWrapper queryCrimes(String whereClause, String[] whereArgs) {
+ Cursor cursor = mDatabase.query(
+ CrimeTable.NAME,
+ null, // Columns - null selects all columns
+ whereClause,
+ whereArgs,
+ null, // groupBy
+ null, // having
+ null // orderBy
+ );
+ return new CrimeCursorWrapper(cursor);
+ }
+
+ private static ContentValues getContentValues(Crime crime) {
+ ContentValues values = new ContentValues();
+ values.put(UUID, crime.getId().toString());
+ values.put(TITLE, crime.getTitle());
+ values.put(DATE, crime.getDate().getTime());
+ values.put(SOLVED, crime.isSolved() ? 1 : 0);
+ return values;
+ }
+
+
+}
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java
new file mode 100644
index 0000000..25557d2
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java
@@ -0,0 +1,11 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.support.v4.app.Fragment;
+
+public class CrimeListActivity extends SingleFragmentActivity {
+
+ @Override
+ protected Fragment createFragment() {
+ return new CrimeListFragment();
+ }
+}
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java
new file mode 100644
index 0000000..80faede
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java
@@ -0,0 +1,186 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.List;
+
+public class CrimeListFragment extends Fragment {
+
+ private static final String SAVED_SUBTITLE_VISIBLE = "subtitle";
+
+ private RecyclerView mCrimeRecyclerView;
+ private CrimeAdapter mAdapter;
+ private boolean mSubtitleVisible;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_crime_list, container, false);
+
+ mCrimeRecyclerView = (RecyclerView) view
+ .findViewById(R.id.crime_recycler_view);
+ mCrimeRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
+
+ if (savedInstanceState != null) {
+ mSubtitleVisible = savedInstanceState.getBoolean(SAVED_SUBTITLE_VISIBLE);
+ }
+
+ updateUI();
+
+ return view;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ updateUI();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(SAVED_SUBTITLE_VISIBLE, mSubtitleVisible);
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ inflater.inflate(R.menu.fragment_crime_list, menu);
+
+ MenuItem subtitleItem = menu.findItem(R.id.show_subtitle);
+ if (mSubtitleVisible) {
+ subtitleItem.setTitle(R.string.hide_subtitle);
+ } else {
+ subtitleItem.setTitle(R.string.show_subtitle);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.new_crime:
+ Crime crime = new Crime();
+ CrimeLab.get(getActivity()).addCrime(crime);
+ Intent intent = CrimePagerActivity
+ .newIntent(getActivity(), crime.getId());
+ startActivity(intent);
+ return true;
+ case R.id.show_subtitle:
+ mSubtitleVisible = !mSubtitleVisible;
+ getActivity().invalidateOptionsMenu();
+ updateSubtitle();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private void updateSubtitle() {
+ CrimeLab crimeLab = CrimeLab.get(getActivity());
+ int crimeCount = crimeLab.getCrimes().size();
+ String subtitle = getString(R.string.subtitle_format, crimeCount);
+
+ if (!mSubtitleVisible) {
+ subtitle = null;
+ }
+
+ AppCompatActivity activity = (AppCompatActivity) getActivity();
+ activity.getSupportActionBar().setSubtitle(subtitle);
+ }
+
+ private void updateUI() {
+ CrimeLab crimeLab = CrimeLab.get(getActivity());
+ List crimes = crimeLab.getCrimes();
+
+ if (mAdapter == null) {
+ mAdapter = new CrimeAdapter(crimes);
+ mCrimeRecyclerView.setAdapter(mAdapter);
+ } else {
+ mAdapter.setCrimes(crimes);
+ mAdapter.notifyDataSetChanged();
+ }
+
+ updateSubtitle();
+ }
+
+ private class CrimeHolder extends RecyclerView.ViewHolder
+ implements View.OnClickListener {
+
+ private Crime mCrime;
+
+ private TextView mTitleTextView;
+ private TextView mDateTextView;
+ private ImageView mSolvedImageView;
+
+ public CrimeHolder(LayoutInflater inflater, ViewGroup parent) {
+ super(inflater.inflate(R.layout.list_item_crime, parent, false));
+ itemView.setOnClickListener(this);
+
+ mTitleTextView = (TextView) itemView.findViewById(R.id.crime_title);
+ mDateTextView = (TextView) itemView.findViewById(R.id.crime_date);
+ mSolvedImageView = (ImageView) itemView.findViewById(R.id.crime_solved);
+ }
+
+ public void bind(Crime crime) {
+ mCrime = crime;
+ mTitleTextView.setText(mCrime.getTitle());
+ mDateTextView.setText(mCrime.getDate().toString());
+ mSolvedImageView.setVisibility(crime.isSolved() ? View.VISIBLE : View.GONE);
+ }
+
+ @Override
+ public void onClick(View view) {
+ Intent intent = CrimePagerActivity.newIntent(getActivity(), mCrime.getId());
+ startActivity(intent);
+ }
+ }
+
+ private class CrimeAdapter extends RecyclerView.Adapter {
+
+ private List mCrimes;
+
+ public CrimeAdapter(List crimes) {
+ mCrimes = crimes;
+ }
+
+ @Override
+ public CrimeHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
+ return new CrimeHolder(layoutInflater, parent);
+ }
+
+ @Override
+ public void onBindViewHolder(CrimeHolder holder, int position) {
+ Crime crime = mCrimes.get(position);
+ holder.bind(crime);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mCrimes.size();
+ }
+
+ public void setCrimes(List crimes) {
+ mCrimes = crimes;
+ }
+ }
+}
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimePagerActivity.java b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimePagerActivity.java
new file mode 100644
index 0000000..b11cca9
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimePagerActivity.java
@@ -0,0 +1,62 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentStatePagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.v7.app.AppCompatActivity;
+
+import java.util.List;
+import java.util.UUID;
+
+public class CrimePagerActivity extends AppCompatActivity {
+
+ private static final String EXTRA_CRIME_ID =
+ "com.bignerdranch.android.criminalintent.crime_id";
+
+ private ViewPager mViewPager;
+ private List mCrimes;
+
+ public static Intent newIntent(Context packageContext, UUID crimeId) {
+ Intent intent = new Intent(packageContext, CrimePagerActivity.class);
+ intent.putExtra(EXTRA_CRIME_ID, crimeId);
+ return intent;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_crime_pager);
+
+ UUID crimeId = (UUID) getIntent()
+ .getSerializableExtra(EXTRA_CRIME_ID);
+
+ mViewPager = (ViewPager) findViewById(R.id.crime_view_pager);
+
+ mCrimes = CrimeLab.get(this).getCrimes();
+ FragmentManager fragmentManager = getSupportFragmentManager();
+ mViewPager.setAdapter(new FragmentStatePagerAdapter(fragmentManager) {
+
+ @Override
+ public Fragment getItem(int position) {
+ Crime crime = mCrimes.get(position);
+ return CrimeFragment.newInstance(crime.getId());
+ }
+
+ @Override
+ public int getCount() {
+ return mCrimes.size();
+ }
+ });
+
+ for (int i = 0; i < mCrimes.size(); i++) {
+ if (mCrimes.get(i).getId().equals(crimeId)) {
+ mViewPager.setCurrentItem(i);
+ break;
+ }
+ }
+ }
+}
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/DatePickerFragment.java b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/DatePickerFragment.java
new file mode 100644
index 0000000..51968c5
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/DatePickerFragment.java
@@ -0,0 +1,80 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+import android.support.v7.app.AlertDialog;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.DatePicker;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+public class DatePickerFragment extends DialogFragment {
+
+ public static final String EXTRA_DATE =
+ "com.bignerdranch.android.criminalintent.date";
+
+ private static final String ARG_DATE = "date";
+
+ private DatePicker mDatePicker;
+
+ public static DatePickerFragment newInstance(Date date) {
+ Bundle args = new Bundle();
+ args.putSerializable(ARG_DATE, date);
+
+ DatePickerFragment fragment = new DatePickerFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ Date date = (Date) getArguments().getSerializable(ARG_DATE);
+
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(date);
+ int year = calendar.get(Calendar.YEAR);
+ int month = calendar.get(Calendar.MONTH);
+ int day = calendar.get(Calendar.DAY_OF_MONTH);
+
+ View v = LayoutInflater.from(getActivity())
+ .inflate(R.layout.dialog_date, null);
+
+ mDatePicker = (DatePicker) v.findViewById(R.id.dialog_date_picker);
+ mDatePicker.init(year, month, day, null);
+
+ return new AlertDialog.Builder(getActivity())
+ .setView(v)
+ .setTitle(R.string.date_picker_title)
+ .setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ int year = mDatePicker.getYear();
+ int month = mDatePicker.getMonth();
+ int day = mDatePicker.getDayOfMonth();
+ Date date = new GregorianCalendar(year, month, day).getTime();
+ sendResult(Activity.RESULT_OK, date);
+ }
+ })
+ .create();
+ }
+
+ private void sendResult(int resultCode, Date date) {
+ if (getTargetFragment() == null) {
+ return;
+ }
+
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_DATE, date);
+
+ getTargetFragment()
+ .onActivityResult(getTargetRequestCode(), resultCode, intent);
+ }
+}
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java
new file mode 100644
index 0000000..9e1d2d8
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java
@@ -0,0 +1,27 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.AppCompatActivity;
+
+public abstract class SingleFragmentActivity extends AppCompatActivity {
+
+ protected abstract Fragment createFragment();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_fragment);
+
+ FragmentManager fm = getSupportFragmentManager();
+ Fragment fragment = fm.findFragmentById(R.id.fragment_container);
+
+ if (fragment == null) {
+ fragment = createFragment();
+ fm.beginTransaction()
+ .add(R.id.fragment_container, fragment)
+ .commit();
+ }
+ }
+}
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeBaseHelper.java b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeBaseHelper.java
new file mode 100644
index 0000000..5462501
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeBaseHelper.java
@@ -0,0 +1,33 @@
+package com.bignerdranch.android.criminalintent.database;
+
+
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+import com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable;
+
+public class CrimeBaseHelper extends SQLiteOpenHelper {
+ private static final int VERSION = 1;
+ private static final String DATABASE_NAME = "crimeBase.db";
+
+ public CrimeBaseHelper(Context context) {
+ super(context, DATABASE_NAME, null, VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL("create table " + CrimeTable.NAME + "(" +
+ " _id integer primary key autoincrement, " +
+ CrimeTable.Cols.UUID + ", " +
+ CrimeTable.Cols.TITLE + ", " +
+ CrimeTable.Cols.DATE + ", " +
+ CrimeTable.Cols.SOLVED +
+ ")"
+ );
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ }
+}
\ No newline at end of file
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeCursorWrapper.java b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeCursorWrapper.java
new file mode 100644
index 0000000..8984d25
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeCursorWrapper.java
@@ -0,0 +1,31 @@
+package com.bignerdranch.android.criminalintent.database;
+
+import android.database.Cursor;
+import android.database.CursorWrapper;
+
+import com.bignerdranch.android.criminalintent.Crime;
+import com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable;
+
+import java.util.Date;
+import java.util.UUID;
+
+public class CrimeCursorWrapper extends CursorWrapper {
+
+ public CrimeCursorWrapper(Cursor cursor) {
+ super(cursor);
+ }
+
+ public Crime getCrime() {
+ String uuidString = getString(getColumnIndex(CrimeTable.Cols.UUID));
+ String title = getString(getColumnIndex(CrimeTable.Cols.TITLE));
+ long date = getLong(getColumnIndex(CrimeTable.Cols.DATE));
+ int isSolved = getInt(getColumnIndex(CrimeTable.Cols.SOLVED));
+
+ Crime crime = new Crime(UUID.fromString(uuidString));
+ crime.setTitle(title);
+ crime.setDate(new Date(date));
+ crime.setSolved(isSolved != 0);
+
+ return crime;
+ }
+}
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeDbSchema.java b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeDbSchema.java
new file mode 100644
index 0000000..69d04dd
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeDbSchema.java
@@ -0,0 +1,14 @@
+package com.bignerdranch.android.criminalintent.database;
+
+public class CrimeDbSchema {
+ public static final class CrimeTable {
+ public static final String NAME = "crimes";
+
+ public static final class Cols {
+ public static final String UUID = "uuid";
+ public static final String TITLE = "title";
+ public static final String DATE = "date";
+ public static final String SOLVED = "solved";
+ }
+ }
+}
\ No newline at end of file
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/drawable-hdpi/ic_menu_add.png b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/drawable-hdpi/ic_menu_add.png
new file mode 100644
index 0000000..1ae5b2d
Binary files /dev/null and b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/drawable-hdpi/ic_menu_add.png differ
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/drawable-hdpi/ic_solved.png b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/drawable-hdpi/ic_solved.png
new file mode 100644
index 0000000..e9b9263
Binary files /dev/null and b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/drawable-hdpi/ic_solved.png differ
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/drawable-mdpi/ic_menu_add.png b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/drawable-mdpi/ic_menu_add.png
new file mode 100644
index 0000000..d51f0dd
Binary files /dev/null and b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/drawable-mdpi/ic_menu_add.png differ
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/drawable-mdpi/ic_solved.png b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/drawable-mdpi/ic_solved.png
new file mode 100644
index 0000000..9548d3c
Binary files /dev/null and b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/drawable-mdpi/ic_solved.png differ
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_menu_add.png b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_menu_add.png
new file mode 100644
index 0000000..9ea0eeb
Binary files /dev/null and b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_menu_add.png differ
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_solved.png b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_solved.png
new file mode 100644
index 0000000..6ab438d
Binary files /dev/null and b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_solved.png differ
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_menu_add.png b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_menu_add.png
new file mode 100644
index 0000000..75f192a
Binary files /dev/null and b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_menu_add.png differ
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_solved.png b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_solved.png
new file mode 100644
index 0000000..ca05191
Binary files /dev/null and b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_solved.png differ
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/drawable-xxxhdpi/ic_solved.png b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/drawable-xxxhdpi/ic_solved.png
new file mode 100644
index 0000000..15d3cf4
Binary files /dev/null and b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/drawable-xxxhdpi/ic_solved.png differ
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/layout/activity_crime_pager.xml b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/layout/activity_crime_pager.xml
new file mode 100644
index 0000000..a80c096
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/layout/activity_crime_pager.xml
@@ -0,0 +1,5 @@
+
+
\ No newline at end of file
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/layout/activity_fragment.xml b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/layout/activity_fragment.xml
new file mode 100644
index 0000000..1773143
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/layout/activity_fragment.xml
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/layout/dialog_date.xml b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/layout/dialog_date.xml
new file mode 100644
index 0000000..c35ec4c
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/layout/dialog_date.xml
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/layout/fragment_crime.xml b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/layout/fragment_crime.xml
new file mode 100644
index 0000000..20734cf
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/layout/fragment_crime.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml
new file mode 100644
index 0000000..4d23b7c
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml
@@ -0,0 +1,5 @@
+
+
\ No newline at end of file
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/layout/list_item_crime.xml b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/layout/list_item_crime.xml
new file mode 100644
index 0000000..e149593
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/layout/list_item_crime.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/menu/fragment_crime_list.xml b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/menu/fragment_crime_list.xml
new file mode 100644
index 0000000..de478c1
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/menu/fragment_crime_list.xml
@@ -0,0 +1,14 @@
+
+
\ No newline at end of file
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/values/colors.xml b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/values/dimens.xml b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/values/strings.xml b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..2b440b4
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/values/strings.xml
@@ -0,0 +1,12 @@
+
+ CriminalIntent
+ Enter a title for the crime.
+ Title
+ Details
+ Solved
+ Date of crime:
+ New Crime
+ Show Subtitle
+ Hide Subtitle
+ %1$d crimes
+
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/values/styles.xml b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java b/Book Files/14_LocalDatabases/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java
new file mode 100644
index 0000000..e0e2773
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.criminalintent;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/build.gradle b/Book Files/14_LocalDatabases/CriminalIntent/build.gradle
new file mode 100644
index 0000000..1ea4bd0
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/gradle.properties b/Book Files/14_LocalDatabases/CriminalIntent/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/gradle/wrapper/gradle-wrapper.jar b/Book Files/14_LocalDatabases/CriminalIntent/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/Book Files/14_LocalDatabases/CriminalIntent/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/gradle/wrapper/gradle-wrapper.properties b/Book Files/14_LocalDatabases/CriminalIntent/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a82392d
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/gradlew b/Book Files/14_LocalDatabases/CriminalIntent/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/gradlew.bat b/Book Files/14_LocalDatabases/CriminalIntent/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/14_LocalDatabases/CriminalIntent/settings.gradle b/Book Files/14_LocalDatabases/CriminalIntent/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/14_LocalDatabases/CriminalIntent/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/14_LocalDatabases/LICENSE.txt b/Book Files/14_LocalDatabases/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Book Files/14_LocalDatabases/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Book Files/15_ImplicitIntents/.DS_Store b/Book Files/15_ImplicitIntents/.DS_Store
new file mode 100755
index 0000000..0b769d5
Binary files /dev/null and b/Book Files/15_ImplicitIntents/.DS_Store differ
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/.gitignore b/Book Files/15_ImplicitIntents/CriminalIntent/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/.idea/compiler.xml b/Book Files/15_ImplicitIntents/CriminalIntent/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/.idea/copyright/profiles_settings.xml b/Book Files/15_ImplicitIntents/CriminalIntent/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/.idea/encodings.xml b/Book Files/15_ImplicitIntents/CriminalIntent/.idea/encodings.xml
new file mode 100644
index 0000000..97626ba
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/.idea/gradle.xml b/Book Files/15_ImplicitIntents/CriminalIntent/.idea/gradle.xml
new file mode 100644
index 0000000..7ac24c7
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/.idea/gradle.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/.idea/misc.xml b/Book Files/15_ImplicitIntents/CriminalIntent/.idea/misc.xml
new file mode 100644
index 0000000..7c1371c
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/.idea/misc.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/.idea/modules.xml b/Book Files/15_ImplicitIntents/CriminalIntent/.idea/modules.xml
new file mode 100644
index 0000000..37be0dd
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/.idea/runConfigurations.xml b/Book Files/15_ImplicitIntents/CriminalIntent/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/.gitignore b/Book Files/15_ImplicitIntents/CriminalIntent/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/build.gradle b/Book Files/15_ImplicitIntents/CriminalIntent/app/build.gradle
new file mode 100644
index 0000000..d05c462
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/app/build.gradle
@@ -0,0 +1,32 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ applicationId "com.bignerdranch.android.criminalintent"
+ minSdkVersion 19
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ testCompile 'junit:junit:4.12'
+ compile 'com.android.support:support-v4:25.3.0'
+ compile 'com.android.support:recyclerview-v7:25.3.0'
+ compile 'com.android.support.constraint:constraint-layout:1.0.2'
+}
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/proguard-rules.pro b/Book Files/15_ImplicitIntents/CriminalIntent/app/proguard-rules.pro
new file mode 100644
index 0000000..99da964
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/chris/AndroidDeveloper/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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 *;
+#}
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..3ed35e7
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.criminalintent;
+
+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.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.bignerdranch.android.criminalintent", appContext.getPackageName());
+ }
+}
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/AndroidManifest.xml b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..215be2b
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/AndroidManifest.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java
new file mode 100644
index 0000000..50f260c
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java
@@ -0,0 +1,58 @@
+package com.bignerdranch.android.criminalintent;
+
+import java.util.Date;
+import java.util.UUID;
+
+public class Crime {
+
+ private UUID mId;
+ private String mTitle;
+ private Date mDate;
+ private boolean mSolved;
+ private String mSuspect;
+
+ public Crime() {
+ this(UUID.randomUUID());
+ }
+
+ public Crime(UUID id) {
+ mId = id;
+ mDate = new Date();
+ }
+
+ public UUID getId() {
+ return mId;
+ }
+
+ public String getTitle() {
+ return mTitle;
+ }
+
+ public void setTitle(String title) {
+ mTitle = title;
+ }
+
+ public Date getDate() {
+ return mDate;
+ }
+
+ public void setDate(Date date) {
+ mDate = date;
+ }
+
+ public boolean isSolved() {
+ return mSolved;
+ }
+
+ public void setSolved(boolean solved) {
+ mSolved = solved;
+ }
+
+ public String getSuspect() {
+ return mSuspect;
+ }
+
+ public void setSuspect(String suspect) {
+ mSuspect = suspect;
+ }
+}
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java
new file mode 100644
index 0000000..c0aa432
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java
@@ -0,0 +1,210 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.text.format.DateFormat;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+
+import java.util.Date;
+import java.util.UUID;
+
+import static android.widget.CompoundButton.*;
+
+public class CrimeFragment extends Fragment {
+
+ private static final String ARG_CRIME_ID = "crime_id";
+ private static final String DIALOG_DATE = "DialogDate";
+
+ private static final int REQUEST_DATE = 0;
+ private static final int REQUEST_CONTACT = 1;
+
+ private Crime mCrime;
+ private EditText mTitleField;
+ private Button mDateButton;
+ private CheckBox mSolvedCheckbox;
+ private Button mReportButton;
+ private Button mSuspectButton;
+
+ public static CrimeFragment newInstance(UUID crimeId) {
+ Bundle args = new Bundle();
+ args.putSerializable(ARG_CRIME_ID, crimeId);
+
+ CrimeFragment fragment = new CrimeFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ UUID crimeId = (UUID) getArguments().getSerializable(ARG_CRIME_ID);
+ mCrime = CrimeLab.get(getActivity()).getCrime(crimeId);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.fragment_crime, container, false);
+
+ mTitleField = (EditText) v.findViewById(R.id.crime_title);
+ mTitleField.setText(mCrime.getTitle());
+ mTitleField.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ mCrime.setTitle(s.toString());
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+
+ }
+ });
+
+ mDateButton = (Button) v.findViewById(R.id.crime_date);
+ updateDate();
+ mDateButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ FragmentManager manager = getFragmentManager();
+ DatePickerFragment dialog = DatePickerFragment
+ .newInstance(mCrime.getDate());
+ dialog.setTargetFragment(CrimeFragment.this, REQUEST_DATE);
+ dialog.show(manager, DIALOG_DATE);
+ }
+ });
+
+ mSolvedCheckbox = (CheckBox) v.findViewById(R.id.crime_solved);
+ mSolvedCheckbox.setChecked(mCrime.isSolved());
+ mSolvedCheckbox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView,
+ boolean isChecked) {
+ mCrime.setSolved(isChecked);
+ }
+ });
+
+ mReportButton = (Button) v.findViewById(R.id.crime_report);
+ mReportButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ Intent i = new Intent(Intent.ACTION_SEND);
+ i.setType("text/plain");
+ i.putExtra(Intent.EXTRA_TEXT, getCrimeReport());
+ i.putExtra(Intent.EXTRA_SUBJECT,
+ getString(R.string.crime_report_subject));
+ i = Intent.createChooser(i, getString(R.string.send_report));
+ startActivity(i);
+ }
+ });
+
+ final Intent pickContact = new Intent(Intent.ACTION_PICK,
+ ContactsContract.Contacts.CONTENT_URI);
+ mSuspectButton = (Button) v.findViewById(R.id.crime_suspect);
+ mSuspectButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ startActivityForResult(pickContact, REQUEST_CONTACT);
+ }
+ });
+ if (mCrime.getSuspect() != null) {
+ mSuspectButton.setText(mCrime.getSuspect());
+ }
+
+ PackageManager packageManager = getActivity().getPackageManager();
+ if (packageManager.resolveActivity(pickContact,
+ PackageManager.MATCH_DEFAULT_ONLY) == null) {
+ mSuspectButton.setEnabled(false);
+ }
+
+ return v;
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ CrimeLab.get(getActivity())
+ .updateCrime(mCrime);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode != Activity.RESULT_OK) {
+ return;
+ }
+
+ if (requestCode == REQUEST_DATE) {
+ Date date = (Date) data
+ .getSerializableExtra(DatePickerFragment.EXTRA_DATE);
+ mCrime.setDate(date);
+ updateDate();
+ } else if (requestCode == REQUEST_CONTACT && data != null) {
+ Uri contactUri = data.getData();
+ // Specify which fields you want your query to return
+ // values for.
+ String[] queryFields = new String[]{
+ ContactsContract.Contacts.DISPLAY_NAME
+ };
+ // Perform your query - the contactUri is like a "where"
+ // clause here
+ Cursor c = getActivity().getContentResolver()
+ .query(contactUri, queryFields, null, null, null);
+ try {
+ // Double-check that you actually got results
+ if (c.getCount() == 0) {
+ return;
+ }
+ // Pull out the first column of the first row of data -
+ // that is your suspect's name.
+ c.moveToFirst();
+ String suspect = c.getString(0);
+ mCrime.setSuspect(suspect);
+ mSuspectButton.setText(suspect);
+ } finally {
+ c.close();
+ }
+ }
+ }
+
+ private void updateDate() {
+ mDateButton.setText(mCrime.getDate().toString());
+ }
+
+ private String getCrimeReport() {
+ String solvedString = null;
+ if (mCrime.isSolved()) {
+ solvedString = getString(R.string.crime_report_solved);
+ } else {
+ solvedString = getString(R.string.crime_report_unsolved);
+ }
+ String dateFormat = "EEE, MMM dd";
+ String dateString = DateFormat.format(dateFormat, mCrime.getDate()).toString();
+ String suspect = mCrime.getSuspect();
+ if (suspect == null) {
+ suspect = getString(R.string.crime_report_no_suspect);
+ } else {
+ suspect = getString(R.string.crime_report_suspect, suspect);
+ }
+ String report = getString(R.string.crime_report,
+ mCrime.getTitle(), dateString, solvedString, suspect);
+ return report;
+ }
+}
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java
new file mode 100644
index 0000000..52f4321
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java
@@ -0,0 +1,107 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+
+import com.bignerdranch.android.criminalintent.database.CrimeBaseHelper;
+import com.bignerdranch.android.criminalintent.database.CrimeCursorWrapper;
+import com.bignerdranch.android.criminalintent.database.CrimeDbSchema;
+import com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import static com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable.*;
+import static com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable.Cols.*;
+
+public class CrimeLab {
+ private static CrimeLab sCrimeLab;
+ private Context mContext;
+ private SQLiteDatabase mDatabase;
+
+ public static CrimeLab get(Context context) {
+ if (sCrimeLab == null) {
+ sCrimeLab = new CrimeLab(context);
+ }
+
+ return sCrimeLab;
+ }
+
+ private CrimeLab(Context context) {
+ mContext = context.getApplicationContext();
+ mDatabase = new CrimeBaseHelper(mContext)
+ .getWritableDatabase();
+
+ }
+
+ public void addCrime(Crime c) {
+ ContentValues values = getContentValues(c);
+ mDatabase.insert(CrimeTable.NAME, null, values);
+ }
+
+ public List getCrimes() {
+ List crimes = new ArrayList<>();
+ CrimeCursorWrapper cursor = queryCrimes(null, null);
+ try {
+ cursor.moveToFirst();
+ while (!cursor.isAfterLast()) {
+ crimes.add(cursor.getCrime());
+ cursor.moveToNext();
+ }
+ } finally {
+ cursor.close();
+ }
+ return crimes;
+ }
+
+ public Crime getCrime(UUID id) {
+ CrimeCursorWrapper cursor = queryCrimes(
+ CrimeTable.Cols.UUID + " = ?",
+ new String[]{id.toString()}
+ );
+ try {
+ if (cursor.getCount() == 0) {
+ return null;
+ }
+ cursor.moveToFirst();
+ return cursor.getCrime();
+ } finally {
+ cursor.close();
+ }
+ }
+
+ public void updateCrime(Crime crime) {
+ String uuidString = crime.getId().toString();
+ ContentValues values = getContentValues(crime);
+ mDatabase.update(CrimeTable.NAME, values,
+ CrimeTable.Cols.UUID + " = ?",
+ new String[]{uuidString});
+ }
+
+ private CrimeCursorWrapper queryCrimes(String whereClause, String[] whereArgs) {
+ Cursor cursor = mDatabase.query(
+ CrimeTable.NAME,
+ null, // Columns - null selects all columns
+ whereClause,
+ whereArgs,
+ null, // groupBy
+ null, // having
+ null // orderBy
+ );
+ return new CrimeCursorWrapper(cursor);
+ }
+
+ private static ContentValues getContentValues(Crime crime) {
+ ContentValues values = new ContentValues();
+ values.put(UUID, crime.getId().toString());
+ values.put(TITLE, crime.getTitle());
+ values.put(DATE, crime.getDate().getTime());
+ values.put(SOLVED, crime.isSolved() ? 1 : 0);
+ values.put(CrimeTable.Cols.SUSPECT, crime.getSuspect());
+
+ return values;
+ }
+}
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java
new file mode 100644
index 0000000..25557d2
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java
@@ -0,0 +1,11 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.support.v4.app.Fragment;
+
+public class CrimeListActivity extends SingleFragmentActivity {
+
+ @Override
+ protected Fragment createFragment() {
+ return new CrimeListFragment();
+ }
+}
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java
new file mode 100644
index 0000000..80faede
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java
@@ -0,0 +1,186 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.List;
+
+public class CrimeListFragment extends Fragment {
+
+ private static final String SAVED_SUBTITLE_VISIBLE = "subtitle";
+
+ private RecyclerView mCrimeRecyclerView;
+ private CrimeAdapter mAdapter;
+ private boolean mSubtitleVisible;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_crime_list, container, false);
+
+ mCrimeRecyclerView = (RecyclerView) view
+ .findViewById(R.id.crime_recycler_view);
+ mCrimeRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
+
+ if (savedInstanceState != null) {
+ mSubtitleVisible = savedInstanceState.getBoolean(SAVED_SUBTITLE_VISIBLE);
+ }
+
+ updateUI();
+
+ return view;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ updateUI();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(SAVED_SUBTITLE_VISIBLE, mSubtitleVisible);
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ inflater.inflate(R.menu.fragment_crime_list, menu);
+
+ MenuItem subtitleItem = menu.findItem(R.id.show_subtitle);
+ if (mSubtitleVisible) {
+ subtitleItem.setTitle(R.string.hide_subtitle);
+ } else {
+ subtitleItem.setTitle(R.string.show_subtitle);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.new_crime:
+ Crime crime = new Crime();
+ CrimeLab.get(getActivity()).addCrime(crime);
+ Intent intent = CrimePagerActivity
+ .newIntent(getActivity(), crime.getId());
+ startActivity(intent);
+ return true;
+ case R.id.show_subtitle:
+ mSubtitleVisible = !mSubtitleVisible;
+ getActivity().invalidateOptionsMenu();
+ updateSubtitle();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private void updateSubtitle() {
+ CrimeLab crimeLab = CrimeLab.get(getActivity());
+ int crimeCount = crimeLab.getCrimes().size();
+ String subtitle = getString(R.string.subtitle_format, crimeCount);
+
+ if (!mSubtitleVisible) {
+ subtitle = null;
+ }
+
+ AppCompatActivity activity = (AppCompatActivity) getActivity();
+ activity.getSupportActionBar().setSubtitle(subtitle);
+ }
+
+ private void updateUI() {
+ CrimeLab crimeLab = CrimeLab.get(getActivity());
+ List crimes = crimeLab.getCrimes();
+
+ if (mAdapter == null) {
+ mAdapter = new CrimeAdapter(crimes);
+ mCrimeRecyclerView.setAdapter(mAdapter);
+ } else {
+ mAdapter.setCrimes(crimes);
+ mAdapter.notifyDataSetChanged();
+ }
+
+ updateSubtitle();
+ }
+
+ private class CrimeHolder extends RecyclerView.ViewHolder
+ implements View.OnClickListener {
+
+ private Crime mCrime;
+
+ private TextView mTitleTextView;
+ private TextView mDateTextView;
+ private ImageView mSolvedImageView;
+
+ public CrimeHolder(LayoutInflater inflater, ViewGroup parent) {
+ super(inflater.inflate(R.layout.list_item_crime, parent, false));
+ itemView.setOnClickListener(this);
+
+ mTitleTextView = (TextView) itemView.findViewById(R.id.crime_title);
+ mDateTextView = (TextView) itemView.findViewById(R.id.crime_date);
+ mSolvedImageView = (ImageView) itemView.findViewById(R.id.crime_solved);
+ }
+
+ public void bind(Crime crime) {
+ mCrime = crime;
+ mTitleTextView.setText(mCrime.getTitle());
+ mDateTextView.setText(mCrime.getDate().toString());
+ mSolvedImageView.setVisibility(crime.isSolved() ? View.VISIBLE : View.GONE);
+ }
+
+ @Override
+ public void onClick(View view) {
+ Intent intent = CrimePagerActivity.newIntent(getActivity(), mCrime.getId());
+ startActivity(intent);
+ }
+ }
+
+ private class CrimeAdapter extends RecyclerView.Adapter {
+
+ private List mCrimes;
+
+ public CrimeAdapter(List crimes) {
+ mCrimes = crimes;
+ }
+
+ @Override
+ public CrimeHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
+ return new CrimeHolder(layoutInflater, parent);
+ }
+
+ @Override
+ public void onBindViewHolder(CrimeHolder holder, int position) {
+ Crime crime = mCrimes.get(position);
+ holder.bind(crime);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mCrimes.size();
+ }
+
+ public void setCrimes(List crimes) {
+ mCrimes = crimes;
+ }
+ }
+}
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimePagerActivity.java b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimePagerActivity.java
new file mode 100644
index 0000000..b11cca9
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimePagerActivity.java
@@ -0,0 +1,62 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentStatePagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.v7.app.AppCompatActivity;
+
+import java.util.List;
+import java.util.UUID;
+
+public class CrimePagerActivity extends AppCompatActivity {
+
+ private static final String EXTRA_CRIME_ID =
+ "com.bignerdranch.android.criminalintent.crime_id";
+
+ private ViewPager mViewPager;
+ private List mCrimes;
+
+ public static Intent newIntent(Context packageContext, UUID crimeId) {
+ Intent intent = new Intent(packageContext, CrimePagerActivity.class);
+ intent.putExtra(EXTRA_CRIME_ID, crimeId);
+ return intent;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_crime_pager);
+
+ UUID crimeId = (UUID) getIntent()
+ .getSerializableExtra(EXTRA_CRIME_ID);
+
+ mViewPager = (ViewPager) findViewById(R.id.crime_view_pager);
+
+ mCrimes = CrimeLab.get(this).getCrimes();
+ FragmentManager fragmentManager = getSupportFragmentManager();
+ mViewPager.setAdapter(new FragmentStatePagerAdapter(fragmentManager) {
+
+ @Override
+ public Fragment getItem(int position) {
+ Crime crime = mCrimes.get(position);
+ return CrimeFragment.newInstance(crime.getId());
+ }
+
+ @Override
+ public int getCount() {
+ return mCrimes.size();
+ }
+ });
+
+ for (int i = 0; i < mCrimes.size(); i++) {
+ if (mCrimes.get(i).getId().equals(crimeId)) {
+ mViewPager.setCurrentItem(i);
+ break;
+ }
+ }
+ }
+}
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/DatePickerFragment.java b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/DatePickerFragment.java
new file mode 100644
index 0000000..51968c5
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/DatePickerFragment.java
@@ -0,0 +1,80 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+import android.support.v7.app.AlertDialog;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.DatePicker;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+public class DatePickerFragment extends DialogFragment {
+
+ public static final String EXTRA_DATE =
+ "com.bignerdranch.android.criminalintent.date";
+
+ private static final String ARG_DATE = "date";
+
+ private DatePicker mDatePicker;
+
+ public static DatePickerFragment newInstance(Date date) {
+ Bundle args = new Bundle();
+ args.putSerializable(ARG_DATE, date);
+
+ DatePickerFragment fragment = new DatePickerFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ Date date = (Date) getArguments().getSerializable(ARG_DATE);
+
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(date);
+ int year = calendar.get(Calendar.YEAR);
+ int month = calendar.get(Calendar.MONTH);
+ int day = calendar.get(Calendar.DAY_OF_MONTH);
+
+ View v = LayoutInflater.from(getActivity())
+ .inflate(R.layout.dialog_date, null);
+
+ mDatePicker = (DatePicker) v.findViewById(R.id.dialog_date_picker);
+ mDatePicker.init(year, month, day, null);
+
+ return new AlertDialog.Builder(getActivity())
+ .setView(v)
+ .setTitle(R.string.date_picker_title)
+ .setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ int year = mDatePicker.getYear();
+ int month = mDatePicker.getMonth();
+ int day = mDatePicker.getDayOfMonth();
+ Date date = new GregorianCalendar(year, month, day).getTime();
+ sendResult(Activity.RESULT_OK, date);
+ }
+ })
+ .create();
+ }
+
+ private void sendResult(int resultCode, Date date) {
+ if (getTargetFragment() == null) {
+ return;
+ }
+
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_DATE, date);
+
+ getTargetFragment()
+ .onActivityResult(getTargetRequestCode(), resultCode, intent);
+ }
+}
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java
new file mode 100644
index 0000000..9e1d2d8
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java
@@ -0,0 +1,27 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.AppCompatActivity;
+
+public abstract class SingleFragmentActivity extends AppCompatActivity {
+
+ protected abstract Fragment createFragment();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_fragment);
+
+ FragmentManager fm = getSupportFragmentManager();
+ Fragment fragment = fm.findFragmentById(R.id.fragment_container);
+
+ if (fragment == null) {
+ fragment = createFragment();
+ fm.beginTransaction()
+ .add(R.id.fragment_container, fragment)
+ .commit();
+ }
+ }
+}
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeBaseHelper.java b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeBaseHelper.java
new file mode 100644
index 0000000..d40467a
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeBaseHelper.java
@@ -0,0 +1,32 @@
+package com.bignerdranch.android.criminalintent.database;
+
+
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+import com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable;
+
+public class CrimeBaseHelper extends SQLiteOpenHelper {
+ private static final int VERSION = 1;
+ private static final String DATABASE_NAME = "crimeBase.db";
+
+ public CrimeBaseHelper(Context context) {
+ super(context, DATABASE_NAME, null, VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL("create table " + CrimeTable.NAME + "(" +
+ " _id integer primary key autoincrement, " + CrimeTable.Cols.UUID + ", " + CrimeTable.Cols.TITLE + ", " +
+ CrimeTable.Cols.DATE + ", " +
+ CrimeTable.Cols.SOLVED + ", " +
+ CrimeTable.Cols.SUSPECT +
+ ")"
+ );
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ }
+}
\ No newline at end of file
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeCursorWrapper.java b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeCursorWrapper.java
new file mode 100644
index 0000000..b341fc0
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeCursorWrapper.java
@@ -0,0 +1,35 @@
+package com.bignerdranch.android.criminalintent.database;
+
+import android.database.Cursor;
+import android.database.CursorWrapper;
+
+import com.bignerdranch.android.criminalintent.Crime;
+import com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable;
+
+import java.util.Date;
+import java.util.UUID;
+
+import static com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable.*;
+
+public class CrimeCursorWrapper extends CursorWrapper {
+
+ public CrimeCursorWrapper(Cursor cursor) {
+ super(cursor);
+ }
+
+ public Crime getCrime() {
+ String uuidString = getString(getColumnIndex(Cols.UUID));
+ String title = getString(getColumnIndex(Cols.TITLE));
+ long date = getLong(getColumnIndex(Cols.DATE));
+ int isSolved = getInt(getColumnIndex(Cols.SOLVED));
+ String suspect = getString(getColumnIndex(CrimeTable.Cols.SUSPECT));
+
+ Crime crime = new Crime(UUID.fromString(uuidString));
+ crime.setTitle(title);
+ crime.setDate(new Date(date));
+ crime.setSolved(isSolved != 0);
+ crime.setSuspect(suspect);
+
+ return crime;
+ }
+}
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeDbSchema.java b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeDbSchema.java
new file mode 100644
index 0000000..88e8d99
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeDbSchema.java
@@ -0,0 +1,15 @@
+package com.bignerdranch.android.criminalintent.database;
+
+public class CrimeDbSchema {
+ public static final class CrimeTable {
+ public static final String NAME = "crimes";
+
+ public static final class Cols {
+ public static final String UUID = "uuid";
+ public static final String TITLE = "title";
+ public static final String DATE = "date";
+ public static final String SOLVED = "solved";
+ public static final String SUSPECT = "suspect";
+ }
+ }
+}
\ No newline at end of file
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/drawable-hdpi/ic_menu_add.png b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/drawable-hdpi/ic_menu_add.png
new file mode 100644
index 0000000..1ae5b2d
Binary files /dev/null and b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/drawable-hdpi/ic_menu_add.png differ
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/drawable-hdpi/ic_solved.png b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/drawable-hdpi/ic_solved.png
new file mode 100644
index 0000000..e9b9263
Binary files /dev/null and b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/drawable-hdpi/ic_solved.png differ
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/drawable-mdpi/ic_menu_add.png b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/drawable-mdpi/ic_menu_add.png
new file mode 100644
index 0000000..d51f0dd
Binary files /dev/null and b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/drawable-mdpi/ic_menu_add.png differ
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/drawable-mdpi/ic_solved.png b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/drawable-mdpi/ic_solved.png
new file mode 100644
index 0000000..9548d3c
Binary files /dev/null and b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/drawable-mdpi/ic_solved.png differ
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_menu_add.png b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_menu_add.png
new file mode 100644
index 0000000..9ea0eeb
Binary files /dev/null and b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_menu_add.png differ
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_solved.png b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_solved.png
new file mode 100644
index 0000000..6ab438d
Binary files /dev/null and b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_solved.png differ
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_menu_add.png b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_menu_add.png
new file mode 100644
index 0000000..75f192a
Binary files /dev/null and b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_menu_add.png differ
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_solved.png b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_solved.png
new file mode 100644
index 0000000..ca05191
Binary files /dev/null and b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_solved.png differ
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/drawable-xxxhdpi/ic_solved.png b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/drawable-xxxhdpi/ic_solved.png
new file mode 100644
index 0000000..15d3cf4
Binary files /dev/null and b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/drawable-xxxhdpi/ic_solved.png differ
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/layout/activity_crime_pager.xml b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/layout/activity_crime_pager.xml
new file mode 100644
index 0000000..a80c096
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/layout/activity_crime_pager.xml
@@ -0,0 +1,5 @@
+
+
\ No newline at end of file
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/layout/activity_fragment.xml b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/layout/activity_fragment.xml
new file mode 100644
index 0000000..1773143
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/layout/activity_fragment.xml
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/layout/dialog_date.xml b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/layout/dialog_date.xml
new file mode 100644
index 0000000..c35ec4c
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/layout/dialog_date.xml
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/layout/fragment_crime.xml b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/layout/fragment_crime.xml
new file mode 100644
index 0000000..77612d4
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/layout/fragment_crime.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml
new file mode 100644
index 0000000..4d23b7c
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml
@@ -0,0 +1,5 @@
+
+
\ No newline at end of file
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/layout/list_item_crime.xml b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/layout/list_item_crime.xml
new file mode 100644
index 0000000..e149593
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/layout/list_item_crime.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/menu/fragment_crime_list.xml b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/menu/fragment_crime_list.xml
new file mode 100644
index 0000000..de478c1
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/menu/fragment_crime_list.xml
@@ -0,0 +1,14 @@
+
+
\ No newline at end of file
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/values/colors.xml b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/values/dimens.xml b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/values/strings.xml b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..2d757e3
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/values/strings.xml
@@ -0,0 +1,23 @@
+
+ CriminalIntent
+ Enter a title for the crime.
+ Title
+ Details
+ Solved
+ Date of crime:
+ New Crime
+ Show Subtitle
+ Hide Subtitle
+ %1$d crimes
+ Choose Suspect
+ Send Crime Report
+ %1$s!
+ The crime was discovered on %2$s. %3$s, and %4$s
+
+ The case is solved
+ The case is not solved
+ there is no suspect.
+ the suspect is %s.
+ CriminalIntent Crime Report
+ Send crime report via
+
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/values/styles.xml b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java
new file mode 100644
index 0000000..e0e2773
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.criminalintent;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/build.gradle b/Book Files/15_ImplicitIntents/CriminalIntent/build.gradle
new file mode 100644
index 0000000..1ea4bd0
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/gradle.properties b/Book Files/15_ImplicitIntents/CriminalIntent/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/gradle/wrapper/gradle-wrapper.jar b/Book Files/15_ImplicitIntents/CriminalIntent/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/Book Files/15_ImplicitIntents/CriminalIntent/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/gradle/wrapper/gradle-wrapper.properties b/Book Files/15_ImplicitIntents/CriminalIntent/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a82392d
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/gradlew b/Book Files/15_ImplicitIntents/CriminalIntent/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/gradlew.bat b/Book Files/15_ImplicitIntents/CriminalIntent/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/15_ImplicitIntents/CriminalIntent/settings.gradle b/Book Files/15_ImplicitIntents/CriminalIntent/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/CriminalIntent/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/15_ImplicitIntents/LICENSE.txt b/Book Files/15_ImplicitIntents/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Book Files/15_ImplicitIntents/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Book Files/16_CameraIntent/CriminalIntent/.gitignore b/Book Files/16_CameraIntent/CriminalIntent/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Book Files/16_CameraIntent/CriminalIntent/.idea/compiler.xml b/Book Files/16_CameraIntent/CriminalIntent/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/16_CameraIntent/CriminalIntent/.idea/copyright/profiles_settings.xml b/Book Files/16_CameraIntent/CriminalIntent/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Book Files/16_CameraIntent/CriminalIntent/.idea/encodings.xml b/Book Files/16_CameraIntent/CriminalIntent/.idea/encodings.xml
new file mode 100644
index 0000000..97626ba
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/16_CameraIntent/CriminalIntent/.idea/gradle.xml b/Book Files/16_CameraIntent/CriminalIntent/.idea/gradle.xml
new file mode 100644
index 0000000..7ac24c7
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/.idea/gradle.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/16_CameraIntent/CriminalIntent/.idea/misc.xml b/Book Files/16_CameraIntent/CriminalIntent/.idea/misc.xml
new file mode 100644
index 0000000..7c1371c
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/.idea/misc.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/16_CameraIntent/CriminalIntent/.idea/modules.xml b/Book Files/16_CameraIntent/CriminalIntent/.idea/modules.xml
new file mode 100644
index 0000000..37be0dd
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/16_CameraIntent/CriminalIntent/.idea/runConfigurations.xml b/Book Files/16_CameraIntent/CriminalIntent/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/.gitignore b/Book Files/16_CameraIntent/CriminalIntent/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/build.gradle b/Book Files/16_CameraIntent/CriminalIntent/app/build.gradle
new file mode 100644
index 0000000..d05c462
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/build.gradle
@@ -0,0 +1,32 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ applicationId "com.bignerdranch.android.criminalintent"
+ minSdkVersion 19
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ testCompile 'junit:junit:4.12'
+ compile 'com.android.support:support-v4:25.3.0'
+ compile 'com.android.support:recyclerview-v7:25.3.0'
+ compile 'com.android.support.constraint:constraint-layout:1.0.2'
+}
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/proguard-rules.pro b/Book Files/16_CameraIntent/CriminalIntent/app/proguard-rules.pro
new file mode 100644
index 0000000..99da964
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/chris/AndroidDeveloper/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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 *;
+#}
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java b/Book Files/16_CameraIntent/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..3ed35e7
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.criminalintent;
+
+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.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.bignerdranch.android.criminalintent", appContext.getPackageName());
+ }
+}
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/AndroidManifest.xml b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..d112f73
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/AndroidManifest.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java
new file mode 100644
index 0000000..a2fe670
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java
@@ -0,0 +1,62 @@
+package com.bignerdranch.android.criminalintent;
+
+import java.util.Date;
+import java.util.UUID;
+
+public class Crime {
+
+ private UUID mId;
+ private String mTitle;
+ private Date mDate;
+ private boolean mSolved;
+ private String mSuspect;
+
+ public Crime() {
+ this(UUID.randomUUID());
+ }
+
+ public Crime(UUID id) {
+ mId = id;
+ mDate = new Date();
+ }
+
+ public UUID getId() {
+ return mId;
+ }
+
+ public String getTitle() {
+ return mTitle;
+ }
+
+ public void setTitle(String title) {
+ mTitle = title;
+ }
+
+ public Date getDate() {
+ return mDate;
+ }
+
+ public void setDate(Date date) {
+ mDate = date;
+ }
+
+ public boolean isSolved() {
+ return mSolved;
+ }
+
+ public void setSolved(boolean solved) {
+ mSolved = solved;
+ }
+
+ public String getSuspect() {
+ return mSuspect;
+ }
+
+ public void setSuspect(String suspect) {
+ mSuspect = suspect;
+ }
+
+ public String getPhotoFilename() {
+ return "IMG_" + getId().toString() + ".jpg";
+ }
+}
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java
new file mode 100644
index 0000000..aa80cac
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java
@@ -0,0 +1,273 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.provider.MediaStore;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.content.FileProvider;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.text.format.DateFormat;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+
+import java.io.File;
+import java.util.Date;
+import java.util.List;
+import java.util.UUID;
+
+import static android.widget.CompoundButton.*;
+
+public class CrimeFragment extends Fragment {
+
+ private static final String ARG_CRIME_ID = "crime_id";
+ private static final String DIALOG_DATE = "DialogDate";
+
+ private static final int REQUEST_DATE = 0;
+ private static final int REQUEST_CONTACT = 1;
+ private static final int REQUEST_PHOTO = 2;
+
+ private Crime mCrime;
+ private File mPhotoFile;
+ private EditText mTitleField;
+ private Button mDateButton;
+ private CheckBox mSolvedCheckbox;
+ private Button mReportButton;
+ private Button mSuspectButton;
+ private ImageButton mPhotoButton;
+ private ImageView mPhotoView;
+
+
+ public static CrimeFragment newInstance(UUID crimeId) {
+ Bundle args = new Bundle();
+ args.putSerializable(ARG_CRIME_ID, crimeId);
+
+ CrimeFragment fragment = new CrimeFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ UUID crimeId = (UUID) getArguments().getSerializable(ARG_CRIME_ID);
+ mCrime = CrimeLab.get(getActivity()).getCrime(crimeId);
+ mPhotoFile = CrimeLab.get(getActivity()).getPhotoFile(mCrime);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.fragment_crime, container, false);
+
+ mTitleField = (EditText) v.findViewById(R.id.crime_title);
+ mTitleField.setText(mCrime.getTitle());
+ mTitleField.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ mCrime.setTitle(s.toString());
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+
+ }
+ });
+
+ mDateButton = (Button) v.findViewById(R.id.crime_date);
+ updateDate();
+ mDateButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ FragmentManager manager = getFragmentManager();
+ DatePickerFragment dialog = DatePickerFragment
+ .newInstance(mCrime.getDate());
+ dialog.setTargetFragment(CrimeFragment.this, REQUEST_DATE);
+ dialog.show(manager, DIALOG_DATE);
+ }
+ });
+
+ mSolvedCheckbox = (CheckBox) v.findViewById(R.id.crime_solved);
+ mSolvedCheckbox.setChecked(mCrime.isSolved());
+ mSolvedCheckbox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView,
+ boolean isChecked) {
+ mCrime.setSolved(isChecked);
+ }
+ });
+
+ mReportButton = (Button) v.findViewById(R.id.crime_report);
+ mReportButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ Intent i = new Intent(Intent.ACTION_SEND);
+ i.setType("text/plain");
+ i.putExtra(Intent.EXTRA_TEXT, getCrimeReport());
+ i.putExtra(Intent.EXTRA_SUBJECT,
+ getString(R.string.crime_report_subject));
+ i = Intent.createChooser(i, getString(R.string.send_report));
+ startActivity(i);
+ }
+ });
+
+ final Intent pickContact = new Intent(Intent.ACTION_PICK,
+ ContactsContract.Contacts.CONTENT_URI);
+ mSuspectButton = (Button) v.findViewById(R.id.crime_suspect);
+ mSuspectButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ startActivityForResult(pickContact, REQUEST_CONTACT);
+ }
+ });
+ if (mCrime.getSuspect() != null) {
+ mSuspectButton.setText(mCrime.getSuspect());
+ }
+
+ PackageManager packageManager = getActivity().getPackageManager();
+ if (packageManager.resolveActivity(pickContact,
+ PackageManager.MATCH_DEFAULT_ONLY) == null) {
+ mSuspectButton.setEnabled(false);
+ }
+
+ mPhotoButton = (ImageButton) v.findViewById(R.id.crime_camera);
+ final Intent captureImage = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
+ boolean canTakePhoto = mPhotoFile != null &&
+ captureImage.resolveActivity(packageManager) != null;
+ mPhotoButton.setEnabled(canTakePhoto);
+
+ mPhotoButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Uri uri = FileProvider.getUriForFile(getActivity(),
+ "com.bignerdranch.android.criminalintent.fileprovider",
+ mPhotoFile);
+ captureImage.putExtra(MediaStore.EXTRA_OUTPUT, uri);
+
+ List cameraActivities = getActivity()
+ .getPackageManager().queryIntentActivities(captureImage,
+ PackageManager.MATCH_DEFAULT_ONLY);
+
+ for (ResolveInfo activity : cameraActivities) {
+ getActivity().grantUriPermission(activity.activityInfo.packageName,
+ uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ }
+
+ startActivityForResult(captureImage, REQUEST_PHOTO);
+ }
+ });
+
+ mPhotoView = (ImageView) v.findViewById(R.id.crime_photo);
+ updatePhotoView();
+
+ return v;
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ CrimeLab.get(getActivity())
+ .updateCrime(mCrime);
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode != Activity.RESULT_OK) {
+ return;
+ }
+
+ if (requestCode == REQUEST_DATE) {
+ Date date = (Date) data
+ .getSerializableExtra(DatePickerFragment.EXTRA_DATE);
+ mCrime.setDate(date);
+ updateDate();
+ } else if (requestCode == REQUEST_CONTACT && data != null) {
+ Uri contactUri = data.getData();
+ // Specify which fields you want your query to return
+ // values for.
+ String[] queryFields = new String[]{
+ ContactsContract.Contacts.DISPLAY_NAME
+ };
+ // Perform your query - the contactUri is like a "where"
+ // clause here
+ Cursor c = getActivity().getContentResolver()
+ .query(contactUri, queryFields, null, null, null);
+ try {
+ // Double-check that you actually got results
+ if (c.getCount() == 0) {
+ return;
+ }
+ // Pull out the first column of the first row of data -
+ // that is your suspect's name.
+ c.moveToFirst();
+ String suspect = c.getString(0);
+ mCrime.setSuspect(suspect);
+ mSuspectButton.setText(suspect);
+ } finally {
+ c.close();
+ }
+ } else if (requestCode == REQUEST_PHOTO) {
+ Uri uri = FileProvider.getUriForFile(getActivity(),
+ "com.bignerdranch.android.criminalintent.fileprovider",
+ mPhotoFile);
+
+ getActivity().revokeUriPermission(uri,
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+ updatePhotoView();
+ }
+ }
+
+ private void updateDate() {
+ mDateButton.setText(mCrime.getDate().toString());
+ }
+
+ private String getCrimeReport() {
+ String solvedString = null;
+ if (mCrime.isSolved()) {
+ solvedString = getString(R.string.crime_report_solved);
+ } else {
+ solvedString = getString(R.string.crime_report_unsolved);
+ }
+ String dateFormat = "EEE, MMM dd";
+ String dateString = DateFormat.format(dateFormat, mCrime.getDate()).toString();
+ String suspect = mCrime.getSuspect();
+ if (suspect == null) {
+ suspect = getString(R.string.crime_report_no_suspect);
+ } else {
+ suspect = getString(R.string.crime_report_suspect, suspect);
+ }
+ String report = getString(R.string.crime_report,
+ mCrime.getTitle(), dateString, solvedString, suspect);
+ return report;
+ }
+
+ private void updatePhotoView() {
+ if (mPhotoFile == null || !mPhotoFile.exists()) {
+ mPhotoView.setImageDrawable(null);
+ } else {
+ Bitmap bitmap = PictureUtils.getScaledBitmap(
+ mPhotoFile.getPath(), getActivity());
+ mPhotoView.setImageBitmap(bitmap);
+ }
+ }
+}
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java
new file mode 100644
index 0000000..b8cd53f
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java
@@ -0,0 +1,113 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+
+import com.bignerdranch.android.criminalintent.database.CrimeBaseHelper;
+import com.bignerdranch.android.criminalintent.database.CrimeCursorWrapper;
+import com.bignerdranch.android.criminalintent.database.CrimeDbSchema;
+import com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import static com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable.*;
+import static com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable.Cols.*;
+
+public class CrimeLab {
+ private static CrimeLab sCrimeLab;
+ private Context mContext;
+ private SQLiteDatabase mDatabase;
+
+ public static CrimeLab get(Context context) {
+ if (sCrimeLab == null) {
+ sCrimeLab = new CrimeLab(context);
+ }
+
+ return sCrimeLab;
+ }
+
+ private CrimeLab(Context context) {
+ mContext = context.getApplicationContext();
+ mDatabase = new CrimeBaseHelper(mContext)
+ .getWritableDatabase();
+
+ }
+
+ public void addCrime(Crime c) {
+ ContentValues values = getContentValues(c);
+ mDatabase.insert(CrimeTable.NAME, null, values);
+ }
+
+ public List getCrimes() {
+ List crimes = new ArrayList<>();
+ CrimeCursorWrapper cursor = queryCrimes(null, null);
+ try {
+ cursor.moveToFirst();
+ while (!cursor.isAfterLast()) {
+ crimes.add(cursor.getCrime());
+ cursor.moveToNext();
+ }
+ } finally {
+ cursor.close();
+ }
+ return crimes;
+ }
+
+ public Crime getCrime(UUID id) {
+ CrimeCursorWrapper cursor = queryCrimes(
+ CrimeTable.Cols.UUID + " = ?",
+ new String[]{id.toString()}
+ );
+ try {
+ if (cursor.getCount() == 0) {
+ return null;
+ }
+ cursor.moveToFirst();
+ return cursor.getCrime();
+ } finally {
+ cursor.close();
+ }
+ }
+
+ public File getPhotoFile(Crime crime) {
+ File filesDir = mContext.getFilesDir();
+ return new File(filesDir, crime.getPhotoFilename());
+ }
+
+ public void updateCrime(Crime crime) {
+ String uuidString = crime.getId().toString();
+ ContentValues values = getContentValues(crime);
+ mDatabase.update(CrimeTable.NAME, values,
+ CrimeTable.Cols.UUID + " = ?",
+ new String[]{uuidString});
+ }
+
+ private CrimeCursorWrapper queryCrimes(String whereClause, String[] whereArgs) {
+ Cursor cursor = mDatabase.query(
+ CrimeTable.NAME,
+ null, // Columns - null selects all columns
+ whereClause,
+ whereArgs,
+ null, // groupBy
+ null, // having
+ null // orderBy
+ );
+ return new CrimeCursorWrapper(cursor);
+ }
+
+ private static ContentValues getContentValues(Crime crime) {
+ ContentValues values = new ContentValues();
+ values.put(UUID, crime.getId().toString());
+ values.put(TITLE, crime.getTitle());
+ values.put(DATE, crime.getDate().getTime());
+ values.put(SOLVED, crime.isSolved() ? 1 : 0);
+ values.put(CrimeTable.Cols.SUSPECT, crime.getSuspect());
+
+ return values;
+ }
+}
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java
new file mode 100644
index 0000000..25557d2
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java
@@ -0,0 +1,11 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.support.v4.app.Fragment;
+
+public class CrimeListActivity extends SingleFragmentActivity {
+
+ @Override
+ protected Fragment createFragment() {
+ return new CrimeListFragment();
+ }
+}
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java
new file mode 100644
index 0000000..80faede
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java
@@ -0,0 +1,186 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.List;
+
+public class CrimeListFragment extends Fragment {
+
+ private static final String SAVED_SUBTITLE_VISIBLE = "subtitle";
+
+ private RecyclerView mCrimeRecyclerView;
+ private CrimeAdapter mAdapter;
+ private boolean mSubtitleVisible;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_crime_list, container, false);
+
+ mCrimeRecyclerView = (RecyclerView) view
+ .findViewById(R.id.crime_recycler_view);
+ mCrimeRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
+
+ if (savedInstanceState != null) {
+ mSubtitleVisible = savedInstanceState.getBoolean(SAVED_SUBTITLE_VISIBLE);
+ }
+
+ updateUI();
+
+ return view;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ updateUI();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(SAVED_SUBTITLE_VISIBLE, mSubtitleVisible);
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ inflater.inflate(R.menu.fragment_crime_list, menu);
+
+ MenuItem subtitleItem = menu.findItem(R.id.show_subtitle);
+ if (mSubtitleVisible) {
+ subtitleItem.setTitle(R.string.hide_subtitle);
+ } else {
+ subtitleItem.setTitle(R.string.show_subtitle);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.new_crime:
+ Crime crime = new Crime();
+ CrimeLab.get(getActivity()).addCrime(crime);
+ Intent intent = CrimePagerActivity
+ .newIntent(getActivity(), crime.getId());
+ startActivity(intent);
+ return true;
+ case R.id.show_subtitle:
+ mSubtitleVisible = !mSubtitleVisible;
+ getActivity().invalidateOptionsMenu();
+ updateSubtitle();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private void updateSubtitle() {
+ CrimeLab crimeLab = CrimeLab.get(getActivity());
+ int crimeCount = crimeLab.getCrimes().size();
+ String subtitle = getString(R.string.subtitle_format, crimeCount);
+
+ if (!mSubtitleVisible) {
+ subtitle = null;
+ }
+
+ AppCompatActivity activity = (AppCompatActivity) getActivity();
+ activity.getSupportActionBar().setSubtitle(subtitle);
+ }
+
+ private void updateUI() {
+ CrimeLab crimeLab = CrimeLab.get(getActivity());
+ List crimes = crimeLab.getCrimes();
+
+ if (mAdapter == null) {
+ mAdapter = new CrimeAdapter(crimes);
+ mCrimeRecyclerView.setAdapter(mAdapter);
+ } else {
+ mAdapter.setCrimes(crimes);
+ mAdapter.notifyDataSetChanged();
+ }
+
+ updateSubtitle();
+ }
+
+ private class CrimeHolder extends RecyclerView.ViewHolder
+ implements View.OnClickListener {
+
+ private Crime mCrime;
+
+ private TextView mTitleTextView;
+ private TextView mDateTextView;
+ private ImageView mSolvedImageView;
+
+ public CrimeHolder(LayoutInflater inflater, ViewGroup parent) {
+ super(inflater.inflate(R.layout.list_item_crime, parent, false));
+ itemView.setOnClickListener(this);
+
+ mTitleTextView = (TextView) itemView.findViewById(R.id.crime_title);
+ mDateTextView = (TextView) itemView.findViewById(R.id.crime_date);
+ mSolvedImageView = (ImageView) itemView.findViewById(R.id.crime_solved);
+ }
+
+ public void bind(Crime crime) {
+ mCrime = crime;
+ mTitleTextView.setText(mCrime.getTitle());
+ mDateTextView.setText(mCrime.getDate().toString());
+ mSolvedImageView.setVisibility(crime.isSolved() ? View.VISIBLE : View.GONE);
+ }
+
+ @Override
+ public void onClick(View view) {
+ Intent intent = CrimePagerActivity.newIntent(getActivity(), mCrime.getId());
+ startActivity(intent);
+ }
+ }
+
+ private class CrimeAdapter extends RecyclerView.Adapter {
+
+ private List mCrimes;
+
+ public CrimeAdapter(List crimes) {
+ mCrimes = crimes;
+ }
+
+ @Override
+ public CrimeHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
+ return new CrimeHolder(layoutInflater, parent);
+ }
+
+ @Override
+ public void onBindViewHolder(CrimeHolder holder, int position) {
+ Crime crime = mCrimes.get(position);
+ holder.bind(crime);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mCrimes.size();
+ }
+
+ public void setCrimes(List crimes) {
+ mCrimes = crimes;
+ }
+ }
+}
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimePagerActivity.java b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimePagerActivity.java
new file mode 100644
index 0000000..b11cca9
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimePagerActivity.java
@@ -0,0 +1,62 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentStatePagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.v7.app.AppCompatActivity;
+
+import java.util.List;
+import java.util.UUID;
+
+public class CrimePagerActivity extends AppCompatActivity {
+
+ private static final String EXTRA_CRIME_ID =
+ "com.bignerdranch.android.criminalintent.crime_id";
+
+ private ViewPager mViewPager;
+ private List mCrimes;
+
+ public static Intent newIntent(Context packageContext, UUID crimeId) {
+ Intent intent = new Intent(packageContext, CrimePagerActivity.class);
+ intent.putExtra(EXTRA_CRIME_ID, crimeId);
+ return intent;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_crime_pager);
+
+ UUID crimeId = (UUID) getIntent()
+ .getSerializableExtra(EXTRA_CRIME_ID);
+
+ mViewPager = (ViewPager) findViewById(R.id.crime_view_pager);
+
+ mCrimes = CrimeLab.get(this).getCrimes();
+ FragmentManager fragmentManager = getSupportFragmentManager();
+ mViewPager.setAdapter(new FragmentStatePagerAdapter(fragmentManager) {
+
+ @Override
+ public Fragment getItem(int position) {
+ Crime crime = mCrimes.get(position);
+ return CrimeFragment.newInstance(crime.getId());
+ }
+
+ @Override
+ public int getCount() {
+ return mCrimes.size();
+ }
+ });
+
+ for (int i = 0; i < mCrimes.size(); i++) {
+ if (mCrimes.get(i).getId().equals(crimeId)) {
+ mViewPager.setCurrentItem(i);
+ break;
+ }
+ }
+ }
+}
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/DatePickerFragment.java b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/DatePickerFragment.java
new file mode 100644
index 0000000..51968c5
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/DatePickerFragment.java
@@ -0,0 +1,80 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+import android.support.v7.app.AlertDialog;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.DatePicker;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+public class DatePickerFragment extends DialogFragment {
+
+ public static final String EXTRA_DATE =
+ "com.bignerdranch.android.criminalintent.date";
+
+ private static final String ARG_DATE = "date";
+
+ private DatePicker mDatePicker;
+
+ public static DatePickerFragment newInstance(Date date) {
+ Bundle args = new Bundle();
+ args.putSerializable(ARG_DATE, date);
+
+ DatePickerFragment fragment = new DatePickerFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ Date date = (Date) getArguments().getSerializable(ARG_DATE);
+
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(date);
+ int year = calendar.get(Calendar.YEAR);
+ int month = calendar.get(Calendar.MONTH);
+ int day = calendar.get(Calendar.DAY_OF_MONTH);
+
+ View v = LayoutInflater.from(getActivity())
+ .inflate(R.layout.dialog_date, null);
+
+ mDatePicker = (DatePicker) v.findViewById(R.id.dialog_date_picker);
+ mDatePicker.init(year, month, day, null);
+
+ return new AlertDialog.Builder(getActivity())
+ .setView(v)
+ .setTitle(R.string.date_picker_title)
+ .setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ int year = mDatePicker.getYear();
+ int month = mDatePicker.getMonth();
+ int day = mDatePicker.getDayOfMonth();
+ Date date = new GregorianCalendar(year, month, day).getTime();
+ sendResult(Activity.RESULT_OK, date);
+ }
+ })
+ .create();
+ }
+
+ private void sendResult(int resultCode, Date date) {
+ if (getTargetFragment() == null) {
+ return;
+ }
+
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_DATE, date);
+
+ getTargetFragment()
+ .onActivityResult(getTargetRequestCode(), resultCode, intent);
+ }
+}
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/PictureUtils.java b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/PictureUtils.java
new file mode 100644
index 0000000..337e2a2
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/PictureUtils.java
@@ -0,0 +1,41 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Point;
+
+public class PictureUtils {
+ public static Bitmap getScaledBitmap(String path, Activity activity) {
+ Point size = new Point();
+ activity.getWindowManager().getDefaultDisplay()
+ .getSize(size);
+ return getScaledBitmap(path, size.x, size.y);
+ }
+
+ public static Bitmap getScaledBitmap(String path, int destWidth, int destHeight) {
+ // Read in the dimensions of the image on disk
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(path, options);
+
+ float srcWidth = options.outWidth;
+ float srcHeight = options.outHeight;
+
+ // Figure out how much to scale down by
+ int inSampleSize = 1;
+ if (srcHeight > destHeight || srcWidth > destWidth) {
+ float heightScale = srcHeight / destHeight;
+ float widthScale = srcWidth / destWidth;
+
+ inSampleSize = Math.round(heightScale > widthScale ? heightScale :
+ widthScale);
+ }
+
+ options = new BitmapFactory.Options();
+ options.inSampleSize = inSampleSize;
+
+ // Read in and create final bitmap
+ return BitmapFactory.decodeFile(path, options);
+ }
+}
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java
new file mode 100644
index 0000000..9e1d2d8
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java
@@ -0,0 +1,27 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.AppCompatActivity;
+
+public abstract class SingleFragmentActivity extends AppCompatActivity {
+
+ protected abstract Fragment createFragment();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_fragment);
+
+ FragmentManager fm = getSupportFragmentManager();
+ Fragment fragment = fm.findFragmentById(R.id.fragment_container);
+
+ if (fragment == null) {
+ fragment = createFragment();
+ fm.beginTransaction()
+ .add(R.id.fragment_container, fragment)
+ .commit();
+ }
+ }
+}
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeBaseHelper.java b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeBaseHelper.java
new file mode 100644
index 0000000..77a8e9f
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeBaseHelper.java
@@ -0,0 +1,34 @@
+package com.bignerdranch.android.criminalintent.database;
+
+
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+import com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable;
+
+public class CrimeBaseHelper extends SQLiteOpenHelper {
+ private static final int VERSION = 1;
+ private static final String DATABASE_NAME = "crimeBase.db";
+
+ public CrimeBaseHelper(Context context) {
+ super(context, DATABASE_NAME, null, VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL("create table " + CrimeTable.NAME + "(" +
+ " _id integer primary key autoincrement, " +
+ CrimeTable.Cols.UUID + ", " +
+ CrimeTable.Cols.TITLE + ", " +
+ CrimeTable.Cols.DATE + ", " +
+ CrimeTable.Cols.SOLVED + ", " +
+ CrimeTable.Cols.SUSPECT +
+ ")"
+ );
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ }
+}
\ No newline at end of file
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeCursorWrapper.java b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeCursorWrapper.java
new file mode 100644
index 0000000..b341fc0
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeCursorWrapper.java
@@ -0,0 +1,35 @@
+package com.bignerdranch.android.criminalintent.database;
+
+import android.database.Cursor;
+import android.database.CursorWrapper;
+
+import com.bignerdranch.android.criminalintent.Crime;
+import com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable;
+
+import java.util.Date;
+import java.util.UUID;
+
+import static com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable.*;
+
+public class CrimeCursorWrapper extends CursorWrapper {
+
+ public CrimeCursorWrapper(Cursor cursor) {
+ super(cursor);
+ }
+
+ public Crime getCrime() {
+ String uuidString = getString(getColumnIndex(Cols.UUID));
+ String title = getString(getColumnIndex(Cols.TITLE));
+ long date = getLong(getColumnIndex(Cols.DATE));
+ int isSolved = getInt(getColumnIndex(Cols.SOLVED));
+ String suspect = getString(getColumnIndex(CrimeTable.Cols.SUSPECT));
+
+ Crime crime = new Crime(UUID.fromString(uuidString));
+ crime.setTitle(title);
+ crime.setDate(new Date(date));
+ crime.setSolved(isSolved != 0);
+ crime.setSuspect(suspect);
+
+ return crime;
+ }
+}
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeDbSchema.java b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeDbSchema.java
new file mode 100644
index 0000000..88e8d99
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeDbSchema.java
@@ -0,0 +1,15 @@
+package com.bignerdranch.android.criminalintent.database;
+
+public class CrimeDbSchema {
+ public static final class CrimeTable {
+ public static final String NAME = "crimes";
+
+ public static final class Cols {
+ public static final String UUID = "uuid";
+ public static final String TITLE = "title";
+ public static final String DATE = "date";
+ public static final String SOLVED = "solved";
+ public static final String SUSPECT = "suspect";
+ }
+ }
+}
\ No newline at end of file
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/drawable-hdpi/ic_menu_add.png b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/drawable-hdpi/ic_menu_add.png
new file mode 100644
index 0000000..1ae5b2d
Binary files /dev/null and b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/drawable-hdpi/ic_menu_add.png differ
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/drawable-hdpi/ic_solved.png b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/drawable-hdpi/ic_solved.png
new file mode 100644
index 0000000..e9b9263
Binary files /dev/null and b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/drawable-hdpi/ic_solved.png differ
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/drawable-mdpi/ic_menu_add.png b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/drawable-mdpi/ic_menu_add.png
new file mode 100644
index 0000000..d51f0dd
Binary files /dev/null and b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/drawable-mdpi/ic_menu_add.png differ
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/drawable-mdpi/ic_solved.png b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/drawable-mdpi/ic_solved.png
new file mode 100644
index 0000000..9548d3c
Binary files /dev/null and b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/drawable-mdpi/ic_solved.png differ
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_menu_add.png b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_menu_add.png
new file mode 100644
index 0000000..9ea0eeb
Binary files /dev/null and b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_menu_add.png differ
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_solved.png b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_solved.png
new file mode 100644
index 0000000..6ab438d
Binary files /dev/null and b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_solved.png differ
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_menu_add.png b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_menu_add.png
new file mode 100644
index 0000000..75f192a
Binary files /dev/null and b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_menu_add.png differ
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_solved.png b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_solved.png
new file mode 100644
index 0000000..ca05191
Binary files /dev/null and b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_solved.png differ
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/drawable-xxxhdpi/ic_solved.png b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/drawable-xxxhdpi/ic_solved.png
new file mode 100644
index 0000000..15d3cf4
Binary files /dev/null and b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/drawable-xxxhdpi/ic_solved.png differ
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/layout/activity_crime_pager.xml b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/layout/activity_crime_pager.xml
new file mode 100644
index 0000000..a80c096
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/layout/activity_crime_pager.xml
@@ -0,0 +1,5 @@
+
+
\ No newline at end of file
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/layout/activity_fragment.xml b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/layout/activity_fragment.xml
new file mode 100644
index 0000000..1773143
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/layout/activity_fragment.xml
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/layout/dialog_date.xml b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/layout/dialog_date.xml
new file mode 100644
index 0000000..c35ec4c
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/layout/dialog_date.xml
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/layout/fragment_crime.xml b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/layout/fragment_crime.xml
new file mode 100644
index 0000000..aaf5122
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/layout/fragment_crime.xml
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml
new file mode 100644
index 0000000..4d23b7c
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml
@@ -0,0 +1,5 @@
+
+
\ No newline at end of file
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/layout/list_item_crime.xml b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/layout/list_item_crime.xml
new file mode 100644
index 0000000..e149593
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/layout/list_item_crime.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/menu/fragment_crime_list.xml b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/menu/fragment_crime_list.xml
new file mode 100644
index 0000000..de478c1
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/menu/fragment_crime_list.xml
@@ -0,0 +1,14 @@
+
+
\ No newline at end of file
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/values/colors.xml b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/values/dimens.xml b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/values/strings.xml b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..2d757e3
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/values/strings.xml
@@ -0,0 +1,23 @@
+
+ CriminalIntent
+ Enter a title for the crime.
+ Title
+ Details
+ Solved
+ Date of crime:
+ New Crime
+ Show Subtitle
+ Hide Subtitle
+ %1$d crimes
+ Choose Suspect
+ Send Crime Report
+ %1$s!
+ The crime was discovered on %2$s. %3$s, and %4$s
+
+ The case is solved
+ The case is not solved
+ there is no suspect.
+ the suspect is %s.
+ CriminalIntent Crime Report
+ Send crime report via
+
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/values/styles.xml b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/xml/files.xml b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/xml/files.xml
new file mode 100644
index 0000000..c27251b
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/src/main/res/xml/files.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/16_CameraIntent/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java b/Book Files/16_CameraIntent/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java
new file mode 100644
index 0000000..e0e2773
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.criminalintent;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/16_CameraIntent/CriminalIntent/build.gradle b/Book Files/16_CameraIntent/CriminalIntent/build.gradle
new file mode 100644
index 0000000..1ea4bd0
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Book Files/16_CameraIntent/CriminalIntent/gradle.properties b/Book Files/16_CameraIntent/CriminalIntent/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/Book Files/16_CameraIntent/CriminalIntent/gradle/wrapper/gradle-wrapper.jar b/Book Files/16_CameraIntent/CriminalIntent/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/Book Files/16_CameraIntent/CriminalIntent/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/16_CameraIntent/CriminalIntent/gradle/wrapper/gradle-wrapper.properties b/Book Files/16_CameraIntent/CriminalIntent/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a82392d
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/16_CameraIntent/CriminalIntent/gradlew b/Book Files/16_CameraIntent/CriminalIntent/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/16_CameraIntent/CriminalIntent/gradlew.bat b/Book Files/16_CameraIntent/CriminalIntent/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/16_CameraIntent/CriminalIntent/settings.gradle b/Book Files/16_CameraIntent/CriminalIntent/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/16_CameraIntent/CriminalIntent/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/16_CameraIntent/LICENSE.txt b/Book Files/16_CameraIntent/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Book Files/16_CameraIntent/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Book Files/17_FragmentComposition/.DS_Store b/Book Files/17_FragmentComposition/.DS_Store
new file mode 100755
index 0000000..e371774
Binary files /dev/null and b/Book Files/17_FragmentComposition/.DS_Store differ
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/.gitignore b/Book Files/17_FragmentComposition/CriminalIntent/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/.idea/compiler.xml b/Book Files/17_FragmentComposition/CriminalIntent/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/.idea/copyright/profiles_settings.xml b/Book Files/17_FragmentComposition/CriminalIntent/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/.idea/encodings.xml b/Book Files/17_FragmentComposition/CriminalIntent/.idea/encodings.xml
new file mode 100644
index 0000000..97626ba
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/.idea/gradle.xml b/Book Files/17_FragmentComposition/CriminalIntent/.idea/gradle.xml
new file mode 100644
index 0000000..7ac24c7
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/.idea/gradle.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/.idea/misc.xml b/Book Files/17_FragmentComposition/CriminalIntent/.idea/misc.xml
new file mode 100644
index 0000000..5d19981
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/.idea/misc.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/.idea/modules.xml b/Book Files/17_FragmentComposition/CriminalIntent/.idea/modules.xml
new file mode 100644
index 0000000..37be0dd
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/.idea/runConfigurations.xml b/Book Files/17_FragmentComposition/CriminalIntent/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/.gitignore b/Book Files/17_FragmentComposition/CriminalIntent/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/build.gradle b/Book Files/17_FragmentComposition/CriminalIntent/app/build.gradle
new file mode 100644
index 0000000..d05c462
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/build.gradle
@@ -0,0 +1,32 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ applicationId "com.bignerdranch.android.criminalintent"
+ minSdkVersion 19
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ testCompile 'junit:junit:4.12'
+ compile 'com.android.support:support-v4:25.3.0'
+ compile 'com.android.support:recyclerview-v7:25.3.0'
+ compile 'com.android.support.constraint:constraint-layout:1.0.2'
+}
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/proguard-rules.pro b/Book Files/17_FragmentComposition/CriminalIntent/app/proguard-rules.pro
new file mode 100644
index 0000000..99da964
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/chris/AndroidDeveloper/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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 *;
+#}
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java b/Book Files/17_FragmentComposition/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..3ed35e7
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/androidTest/java/com/bignerdranch/android/criminalintent/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.criminalintent;
+
+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.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.bignerdranch.android.criminalintent", appContext.getPackageName());
+ }
+}
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/AndroidManifest.xml b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..09ad082
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/AndroidManifest.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java
new file mode 100644
index 0000000..a2fe670
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/Crime.java
@@ -0,0 +1,62 @@
+package com.bignerdranch.android.criminalintent;
+
+import java.util.Date;
+import java.util.UUID;
+
+public class Crime {
+
+ private UUID mId;
+ private String mTitle;
+ private Date mDate;
+ private boolean mSolved;
+ private String mSuspect;
+
+ public Crime() {
+ this(UUID.randomUUID());
+ }
+
+ public Crime(UUID id) {
+ mId = id;
+ mDate = new Date();
+ }
+
+ public UUID getId() {
+ return mId;
+ }
+
+ public String getTitle() {
+ return mTitle;
+ }
+
+ public void setTitle(String title) {
+ mTitle = title;
+ }
+
+ public Date getDate() {
+ return mDate;
+ }
+
+ public void setDate(Date date) {
+ mDate = date;
+ }
+
+ public boolean isSolved() {
+ return mSolved;
+ }
+
+ public void setSolved(boolean solved) {
+ mSolved = solved;
+ }
+
+ public String getSuspect() {
+ return mSuspect;
+ }
+
+ public void setSuspect(String suspect) {
+ mSuspect = suspect;
+ }
+
+ public String getPhotoFilename() {
+ return "IMG_" + getId().toString() + ".jpg";
+ }
+}
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java
new file mode 100644
index 0000000..6bf0634
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeFragment.java
@@ -0,0 +1,304 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.provider.MediaStore;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.content.FileProvider;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.text.format.DateFormat;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+
+import java.io.File;
+import java.util.Date;
+import java.util.List;
+import java.util.UUID;
+
+import static android.widget.CompoundButton.OnCheckedChangeListener;
+
+public class CrimeFragment extends Fragment {
+
+ private static final String ARG_CRIME_ID = "crime_id";
+ private static final String DIALOG_DATE = "DialogDate";
+
+ private static final int REQUEST_DATE = 0;
+ private static final int REQUEST_CONTACT = 1;
+ private static final int REQUEST_PHOTO = 2;
+
+ private Crime mCrime;
+ private File mPhotoFile;
+ private EditText mTitleField;
+ private Button mDateButton;
+ private CheckBox mSolvedCheckbox;
+ private Button mReportButton;
+ private Button mSuspectButton;
+ private ImageButton mPhotoButton;
+ private ImageView mPhotoView;
+ private Callbacks mCallbacks;
+
+ /**
+ * Required interface for hosting activities.
+ */
+ public interface Callbacks {
+ void onCrimeUpdated(Crime crime);
+ }
+
+
+ public static CrimeFragment newInstance(UUID crimeId) {
+ Bundle args = new Bundle();
+ args.putSerializable(ARG_CRIME_ID, crimeId);
+
+ CrimeFragment fragment = new CrimeFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ mCallbacks = (Callbacks) context;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ UUID crimeId = (UUID) getArguments().getSerializable(ARG_CRIME_ID);
+ mCrime = CrimeLab.get(getActivity()).getCrime(crimeId);
+ mPhotoFile = CrimeLab.get(getActivity()).getPhotoFile(mCrime);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.fragment_crime, container, false);
+
+ mTitleField = (EditText) v.findViewById(R.id.crime_title);
+ mTitleField.setText(mCrime.getTitle());
+ mTitleField.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ mCrime.setTitle(s.toString());
+ updateCrime();
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+
+ }
+ });
+
+ mDateButton = (Button) v.findViewById(R.id.crime_date);
+ updateDate();
+ mDateButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ FragmentManager manager = getFragmentManager();
+ DatePickerFragment dialog = DatePickerFragment
+ .newInstance(mCrime.getDate());
+ dialog.setTargetFragment(CrimeFragment.this, REQUEST_DATE);
+ dialog.show(manager, DIALOG_DATE);
+ }
+ });
+
+ mSolvedCheckbox = (CheckBox) v.findViewById(R.id.crime_solved);
+ mSolvedCheckbox.setChecked(mCrime.isSolved());
+ mSolvedCheckbox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView,
+ boolean isChecked) {
+ mCrime.setSolved(isChecked);
+ updateCrime();
+ }
+ });
+
+ mReportButton = (Button) v.findViewById(R.id.crime_report);
+ mReportButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ Intent i = new Intent(Intent.ACTION_SEND);
+ i.setType("text/plain");
+ i.putExtra(Intent.EXTRA_TEXT, getCrimeReport());
+ i.putExtra(Intent.EXTRA_SUBJECT,
+ getString(R.string.crime_report_subject));
+ i = Intent.createChooser(i, getString(R.string.send_report));
+ startActivity(i);
+ }
+ });
+
+ final Intent pickContact = new Intent(Intent.ACTION_PICK,
+ ContactsContract.Contacts.CONTENT_URI);
+ mSuspectButton = (Button) v.findViewById(R.id.crime_suspect);
+ mSuspectButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ startActivityForResult(pickContact, REQUEST_CONTACT);
+ }
+ });
+ if (mCrime.getSuspect() != null) {
+ mSuspectButton.setText(mCrime.getSuspect());
+ }
+
+ PackageManager packageManager = getActivity().getPackageManager();
+ if (packageManager.resolveActivity(pickContact,
+ PackageManager.MATCH_DEFAULT_ONLY) == null) {
+ mSuspectButton.setEnabled(false);
+ }
+
+ mPhotoButton = (ImageButton) v.findViewById(R.id.crime_camera);
+ final Intent captureImage = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
+ boolean canTakePhoto = mPhotoFile != null &&
+ captureImage.resolveActivity(packageManager) != null;
+ mPhotoButton.setEnabled(canTakePhoto);
+
+ mPhotoButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Uri uri = FileProvider.getUriForFile(getActivity(),
+ "com.bignerdranch.android.criminalintent.fileprovider",
+ mPhotoFile);
+ captureImage.putExtra(MediaStore.EXTRA_OUTPUT, uri);
+
+ List cameraActivities = getActivity()
+ .getPackageManager().queryIntentActivities(captureImage,
+ PackageManager.MATCH_DEFAULT_ONLY);
+
+ for (ResolveInfo activity : cameraActivities) {
+ getActivity().grantUriPermission(activity.activityInfo.packageName,
+ uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+ }
+
+ startActivityForResult(captureImage, REQUEST_PHOTO);
+ }
+ });
+
+ mPhotoView = (ImageView) v.findViewById(R.id.crime_photo);
+ updatePhotoView();
+
+ return v;
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+
+ CrimeLab.get(getActivity())
+ .updateCrime(mCrime);
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ mCallbacks = null;
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode != Activity.RESULT_OK) {
+ return;
+ }
+
+ if (requestCode == REQUEST_DATE) {
+ Date date = (Date) data
+ .getSerializableExtra(DatePickerFragment.EXTRA_DATE);
+ mCrime.setDate(date);
+ updateCrime();
+ updateDate();
+ } else if (requestCode == REQUEST_CONTACT && data != null) {
+ Uri contactUri = data.getData();
+ // Specify which fields you want your query to return
+ // values for.
+ String[] queryFields = new String[]{
+ ContactsContract.Contacts.DISPLAY_NAME
+ };
+ // Perform your query - the contactUri is like a "where"
+ // clause here
+ Cursor c = getActivity().getContentResolver()
+ .query(contactUri, queryFields, null, null, null);
+ try {
+ // Double-check that you actually got results
+ if (c.getCount() == 0) {
+ return;
+ }
+ // Pull out the first column of the first row of data -
+ // that is your suspect's name.
+ c.moveToFirst();
+ String suspect = c.getString(0);
+ mCrime.setSuspect(suspect);
+ updateCrime();
+ mSuspectButton.setText(suspect);
+ } finally {
+ c.close();
+ }
+ } else if (requestCode == REQUEST_PHOTO) {
+ Uri uri = FileProvider.getUriForFile(getActivity(),
+ "com.bignerdranch.android.criminalintent.fileprovider",
+ mPhotoFile);
+
+ getActivity().revokeUriPermission(uri,
+ Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+
+ updateCrime();
+ updatePhotoView();
+ }
+ }
+
+ private void updateCrime() {
+ CrimeLab.get(getActivity()).updateCrime(mCrime);
+ mCallbacks.onCrimeUpdated(mCrime);
+ }
+
+ private void updateDate() {
+ mDateButton.setText(mCrime.getDate().toString());
+ }
+
+ private String getCrimeReport() {
+ String solvedString = null;
+ if (mCrime.isSolved()) {
+ solvedString = getString(R.string.crime_report_solved);
+ } else {
+ solvedString = getString(R.string.crime_report_unsolved);
+ }
+ String dateFormat = "EEE, MMM dd";
+ String dateString = DateFormat.format(dateFormat, mCrime.getDate()).toString();
+ String suspect = mCrime.getSuspect();
+ if (suspect == null) {
+ suspect = getString(R.string.crime_report_no_suspect);
+ } else {
+ suspect = getString(R.string.crime_report_suspect, suspect);
+ }
+ String report = getString(R.string.crime_report,
+ mCrime.getTitle(), dateString, solvedString, suspect);
+ return report;
+ }
+
+ private void updatePhotoView() {
+ if (mPhotoFile == null || !mPhotoFile.exists()) {
+ mPhotoView.setImageDrawable(null);
+ } else {
+ Bitmap bitmap = PictureUtils.getScaledBitmap(
+ mPhotoFile.getPath(), getActivity());
+ mPhotoView.setImageBitmap(bitmap);
+ }
+ }
+}
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java
new file mode 100644
index 0000000..b8cd53f
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeLab.java
@@ -0,0 +1,113 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+
+import com.bignerdranch.android.criminalintent.database.CrimeBaseHelper;
+import com.bignerdranch.android.criminalintent.database.CrimeCursorWrapper;
+import com.bignerdranch.android.criminalintent.database.CrimeDbSchema;
+import com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import static com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable.*;
+import static com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable.Cols.*;
+
+public class CrimeLab {
+ private static CrimeLab sCrimeLab;
+ private Context mContext;
+ private SQLiteDatabase mDatabase;
+
+ public static CrimeLab get(Context context) {
+ if (sCrimeLab == null) {
+ sCrimeLab = new CrimeLab(context);
+ }
+
+ return sCrimeLab;
+ }
+
+ private CrimeLab(Context context) {
+ mContext = context.getApplicationContext();
+ mDatabase = new CrimeBaseHelper(mContext)
+ .getWritableDatabase();
+
+ }
+
+ public void addCrime(Crime c) {
+ ContentValues values = getContentValues(c);
+ mDatabase.insert(CrimeTable.NAME, null, values);
+ }
+
+ public List getCrimes() {
+ List crimes = new ArrayList<>();
+ CrimeCursorWrapper cursor = queryCrimes(null, null);
+ try {
+ cursor.moveToFirst();
+ while (!cursor.isAfterLast()) {
+ crimes.add(cursor.getCrime());
+ cursor.moveToNext();
+ }
+ } finally {
+ cursor.close();
+ }
+ return crimes;
+ }
+
+ public Crime getCrime(UUID id) {
+ CrimeCursorWrapper cursor = queryCrimes(
+ CrimeTable.Cols.UUID + " = ?",
+ new String[]{id.toString()}
+ );
+ try {
+ if (cursor.getCount() == 0) {
+ return null;
+ }
+ cursor.moveToFirst();
+ return cursor.getCrime();
+ } finally {
+ cursor.close();
+ }
+ }
+
+ public File getPhotoFile(Crime crime) {
+ File filesDir = mContext.getFilesDir();
+ return new File(filesDir, crime.getPhotoFilename());
+ }
+
+ public void updateCrime(Crime crime) {
+ String uuidString = crime.getId().toString();
+ ContentValues values = getContentValues(crime);
+ mDatabase.update(CrimeTable.NAME, values,
+ CrimeTable.Cols.UUID + " = ?",
+ new String[]{uuidString});
+ }
+
+ private CrimeCursorWrapper queryCrimes(String whereClause, String[] whereArgs) {
+ Cursor cursor = mDatabase.query(
+ CrimeTable.NAME,
+ null, // Columns - null selects all columns
+ whereClause,
+ whereArgs,
+ null, // groupBy
+ null, // having
+ null // orderBy
+ );
+ return new CrimeCursorWrapper(cursor);
+ }
+
+ private static ContentValues getContentValues(Crime crime) {
+ ContentValues values = new ContentValues();
+ values.put(UUID, crime.getId().toString());
+ values.put(TITLE, crime.getTitle());
+ values.put(DATE, crime.getDate().getTime());
+ values.put(SOLVED, crime.isSolved() ? 1 : 0);
+ values.put(CrimeTable.Cols.SUSPECT, crime.getSuspect());
+
+ return values;
+ }
+}
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java
new file mode 100644
index 0000000..2728b25
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListActivity.java
@@ -0,0 +1,38 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.content.Intent;
+import android.support.v4.app.Fragment;
+
+public class CrimeListActivity extends SingleFragmentActivity
+ implements CrimeListFragment.Callbacks, CrimeFragment.Callbacks {
+
+ @Override
+ protected Fragment createFragment() {
+ return new CrimeListFragment();
+ }
+
+ @Override
+ protected int getLayoutResId() {
+ return R.layout.activity_masterdetail;
+ }
+
+ @Override
+ public void onCrimeSelected(Crime crime) {
+ if (findViewById(R.id.detail_fragment_container) == null) {
+ Intent intent = CrimePagerActivity.newIntent(this, crime.getId());
+ startActivity(intent);
+ } else {
+ Fragment newDetail = CrimeFragment.newInstance(crime.getId());
+ getSupportFragmentManager().beginTransaction()
+ .replace(R.id.detail_fragment_container, newDetail)
+ .commit();
+ }
+ }
+
+ public void onCrimeUpdated(Crime crime) {
+ CrimeListFragment listFragment = (CrimeListFragment)
+ getSupportFragmentManager()
+ .findFragmentById(R.id.fragment_container);
+ listFragment.updateUI();
+ }
+}
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java
new file mode 100644
index 0000000..01bce0c
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimeListFragment.java
@@ -0,0 +1,205 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.util.List;
+
+public class CrimeListFragment extends Fragment {
+
+ private static final String SAVED_SUBTITLE_VISIBLE = "subtitle";
+
+ private RecyclerView mCrimeRecyclerView;
+ private CrimeAdapter mAdapter;
+ private boolean mSubtitleVisible;
+ private Callbacks mCallbacks;
+
+ /**
+ * Required interface for hosting activities.
+ */
+ public interface Callbacks {
+ void onCrimeSelected(Crime crime);
+ }
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+ mCallbacks = (Callbacks) context;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_crime_list, container, false);
+
+ mCrimeRecyclerView = (RecyclerView) view
+ .findViewById(R.id.crime_recycler_view);
+ mCrimeRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
+
+ if (savedInstanceState != null) {
+ mSubtitleVisible = savedInstanceState.getBoolean(SAVED_SUBTITLE_VISIBLE);
+ }
+
+ updateUI();
+
+ return view;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ updateUI();
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(SAVED_SUBTITLE_VISIBLE, mSubtitleVisible);
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ mCallbacks = null;
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ inflater.inflate(R.menu.fragment_crime_list, menu);
+
+ MenuItem subtitleItem = menu.findItem(R.id.show_subtitle);
+ if (mSubtitleVisible) {
+ subtitleItem.setTitle(R.string.hide_subtitle);
+ } else {
+ subtitleItem.setTitle(R.string.show_subtitle);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.new_crime:
+ Crime crime = new Crime();
+ CrimeLab.get(getActivity()).addCrime(crime);
+ updateUI();
+ mCallbacks.onCrimeSelected(crime);
+ return true;
+ case R.id.show_subtitle:
+ mSubtitleVisible = !mSubtitleVisible;
+ getActivity().invalidateOptionsMenu();
+ updateSubtitle();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private void updateSubtitle() {
+ CrimeLab crimeLab = CrimeLab.get(getActivity());
+ int crimeCount = crimeLab.getCrimes().size();
+ String subtitle = getString(R.string.subtitle_format, crimeCount);
+
+ if (!mSubtitleVisible) {
+ subtitle = null;
+ }
+
+ AppCompatActivity activity = (AppCompatActivity) getActivity();
+ activity.getSupportActionBar().setSubtitle(subtitle);
+ }
+
+ public void updateUI() {
+ CrimeLab crimeLab = CrimeLab.get(getActivity());
+ List crimes = crimeLab.getCrimes();
+
+ if (mAdapter == null) {
+ mAdapter = new CrimeAdapter(crimes);
+ mCrimeRecyclerView.setAdapter(mAdapter);
+ } else {
+ mAdapter.setCrimes(crimes);
+ mAdapter.notifyDataSetChanged();
+ }
+
+ updateSubtitle();
+ }
+
+ private class CrimeHolder extends RecyclerView.ViewHolder
+ implements View.OnClickListener {
+
+ private Crime mCrime;
+
+ private TextView mTitleTextView;
+ private TextView mDateTextView;
+ private ImageView mSolvedImageView;
+
+ public CrimeHolder(LayoutInflater inflater, ViewGroup parent) {
+ super(inflater.inflate(R.layout.list_item_crime, parent, false));
+ itemView.setOnClickListener(this);
+
+ mTitleTextView = (TextView) itemView.findViewById(R.id.crime_title);
+ mDateTextView = (TextView) itemView.findViewById(R.id.crime_date);
+ mSolvedImageView = (ImageView) itemView.findViewById(R.id.crime_solved);
+ }
+
+ public void bind(Crime crime) {
+ mCrime = crime;
+ mTitleTextView.setText(mCrime.getTitle());
+ mDateTextView.setText(mCrime.getDate().toString());
+ mSolvedImageView.setVisibility(crime.isSolved() ? View.VISIBLE : View.GONE);
+ }
+
+ @Override
+ public void onClick(View view) {
+ mCallbacks.onCrimeSelected(mCrime);
+ }
+ }
+
+ private class CrimeAdapter extends RecyclerView.Adapter {
+
+ private List mCrimes;
+
+ public CrimeAdapter(List crimes) {
+ mCrimes = crimes;
+ }
+
+ @Override
+ public CrimeHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
+ return new CrimeHolder(layoutInflater, parent);
+ }
+
+ @Override
+ public void onBindViewHolder(CrimeHolder holder, int position) {
+ Crime crime = mCrimes.get(position);
+ holder.bind(crime);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mCrimes.size();
+ }
+
+ public void setCrimes(List crimes) {
+ mCrimes = crimes;
+ }
+ }
+}
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimePagerActivity.java b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimePagerActivity.java
new file mode 100644
index 0000000..ff532bd
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/CrimePagerActivity.java
@@ -0,0 +1,68 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentStatePagerAdapter;
+import android.support.v4.view.ViewPager;
+import android.support.v7.app.AppCompatActivity;
+
+import java.util.List;
+import java.util.UUID;
+
+public class CrimePagerActivity extends AppCompatActivity
+ implements CrimeFragment.Callbacks {
+
+ private static final String EXTRA_CRIME_ID =
+ "com.bignerdranch.android.criminalintent.crime_id";
+
+ private ViewPager mViewPager;
+ private List mCrimes;
+
+ public static Intent newIntent(Context packageContext, UUID crimeId) {
+ Intent intent = new Intent(packageContext, CrimePagerActivity.class);
+ intent.putExtra(EXTRA_CRIME_ID, crimeId);
+ return intent;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_crime_pager);
+
+ UUID crimeId = (UUID) getIntent()
+ .getSerializableExtra(EXTRA_CRIME_ID);
+
+ mViewPager = (ViewPager) findViewById(R.id.crime_view_pager);
+
+ mCrimes = CrimeLab.get(this).getCrimes();
+ FragmentManager fragmentManager = getSupportFragmentManager();
+ mViewPager.setAdapter(new FragmentStatePagerAdapter(fragmentManager) {
+
+ @Override
+ public Fragment getItem(int position) {
+ Crime crime = mCrimes.get(position);
+ return CrimeFragment.newInstance(crime.getId());
+ }
+
+ @Override
+ public int getCount() {
+ return mCrimes.size();
+ }
+ });
+
+ for (int i = 0; i < mCrimes.size(); i++) {
+ if (mCrimes.get(i).getId().equals(crimeId)) {
+ mViewPager.setCurrentItem(i);
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void onCrimeUpdated(Crime crime) {
+
+ }
+}
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/DatePickerFragment.java b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/DatePickerFragment.java
new file mode 100644
index 0000000..51968c5
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/DatePickerFragment.java
@@ -0,0 +1,80 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+import android.support.v7.app.AlertDialog;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.DatePicker;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+public class DatePickerFragment extends DialogFragment {
+
+ public static final String EXTRA_DATE =
+ "com.bignerdranch.android.criminalintent.date";
+
+ private static final String ARG_DATE = "date";
+
+ private DatePicker mDatePicker;
+
+ public static DatePickerFragment newInstance(Date date) {
+ Bundle args = new Bundle();
+ args.putSerializable(ARG_DATE, date);
+
+ DatePickerFragment fragment = new DatePickerFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ Date date = (Date) getArguments().getSerializable(ARG_DATE);
+
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTime(date);
+ int year = calendar.get(Calendar.YEAR);
+ int month = calendar.get(Calendar.MONTH);
+ int day = calendar.get(Calendar.DAY_OF_MONTH);
+
+ View v = LayoutInflater.from(getActivity())
+ .inflate(R.layout.dialog_date, null);
+
+ mDatePicker = (DatePicker) v.findViewById(R.id.dialog_date_picker);
+ mDatePicker.init(year, month, day, null);
+
+ return new AlertDialog.Builder(getActivity())
+ .setView(v)
+ .setTitle(R.string.date_picker_title)
+ .setPositiveButton(android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ int year = mDatePicker.getYear();
+ int month = mDatePicker.getMonth();
+ int day = mDatePicker.getDayOfMonth();
+ Date date = new GregorianCalendar(year, month, day).getTime();
+ sendResult(Activity.RESULT_OK, date);
+ }
+ })
+ .create();
+ }
+
+ private void sendResult(int resultCode, Date date) {
+ if (getTargetFragment() == null) {
+ return;
+ }
+
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_DATE, date);
+
+ getTargetFragment()
+ .onActivityResult(getTargetRequestCode(), resultCode, intent);
+ }
+}
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/PictureUtils.java b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/PictureUtils.java
new file mode 100644
index 0000000..337e2a2
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/PictureUtils.java
@@ -0,0 +1,41 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Point;
+
+public class PictureUtils {
+ public static Bitmap getScaledBitmap(String path, Activity activity) {
+ Point size = new Point();
+ activity.getWindowManager().getDefaultDisplay()
+ .getSize(size);
+ return getScaledBitmap(path, size.x, size.y);
+ }
+
+ public static Bitmap getScaledBitmap(String path, int destWidth, int destHeight) {
+ // Read in the dimensions of the image on disk
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(path, options);
+
+ float srcWidth = options.outWidth;
+ float srcHeight = options.outHeight;
+
+ // Figure out how much to scale down by
+ int inSampleSize = 1;
+ if (srcHeight > destHeight || srcWidth > destWidth) {
+ float heightScale = srcHeight / destHeight;
+ float widthScale = srcWidth / destWidth;
+
+ inSampleSize = Math.round(heightScale > widthScale ? heightScale :
+ widthScale);
+ }
+
+ options = new BitmapFactory.Options();
+ options.inSampleSize = inSampleSize;
+
+ // Read in and create final bitmap
+ return BitmapFactory.decodeFile(path, options);
+ }
+}
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java
new file mode 100644
index 0000000..e43329d
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/SingleFragmentActivity.java
@@ -0,0 +1,33 @@
+package com.bignerdranch.android.criminalintent;
+
+import android.os.Bundle;
+import android.support.annotation.LayoutRes;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.AppCompatActivity;
+
+public abstract class SingleFragmentActivity extends AppCompatActivity {
+
+ protected abstract Fragment createFragment();
+
+ @LayoutRes
+ protected int getLayoutResId() {
+ return R.layout.activity_fragment;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(getLayoutResId());
+
+ FragmentManager fm = getSupportFragmentManager();
+ Fragment fragment = fm.findFragmentById(R.id.fragment_container);
+
+ if (fragment == null) {
+ fragment = createFragment();
+ fm.beginTransaction()
+ .add(R.id.fragment_container, fragment)
+ .commit();
+ }
+ }
+}
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeBaseHelper.java b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeBaseHelper.java
new file mode 100644
index 0000000..77a8e9f
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeBaseHelper.java
@@ -0,0 +1,34 @@
+package com.bignerdranch.android.criminalintent.database;
+
+
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+
+import com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable;
+
+public class CrimeBaseHelper extends SQLiteOpenHelper {
+ private static final int VERSION = 1;
+ private static final String DATABASE_NAME = "crimeBase.db";
+
+ public CrimeBaseHelper(Context context) {
+ super(context, DATABASE_NAME, null, VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL("create table " + CrimeTable.NAME + "(" +
+ " _id integer primary key autoincrement, " +
+ CrimeTable.Cols.UUID + ", " +
+ CrimeTable.Cols.TITLE + ", " +
+ CrimeTable.Cols.DATE + ", " +
+ CrimeTable.Cols.SOLVED + ", " +
+ CrimeTable.Cols.SUSPECT +
+ ")"
+ );
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ }
+}
\ No newline at end of file
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeCursorWrapper.java b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeCursorWrapper.java
new file mode 100644
index 0000000..b341fc0
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeCursorWrapper.java
@@ -0,0 +1,35 @@
+package com.bignerdranch.android.criminalintent.database;
+
+import android.database.Cursor;
+import android.database.CursorWrapper;
+
+import com.bignerdranch.android.criminalintent.Crime;
+import com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable;
+
+import java.util.Date;
+import java.util.UUID;
+
+import static com.bignerdranch.android.criminalintent.database.CrimeDbSchema.CrimeTable.*;
+
+public class CrimeCursorWrapper extends CursorWrapper {
+
+ public CrimeCursorWrapper(Cursor cursor) {
+ super(cursor);
+ }
+
+ public Crime getCrime() {
+ String uuidString = getString(getColumnIndex(Cols.UUID));
+ String title = getString(getColumnIndex(Cols.TITLE));
+ long date = getLong(getColumnIndex(Cols.DATE));
+ int isSolved = getInt(getColumnIndex(Cols.SOLVED));
+ String suspect = getString(getColumnIndex(CrimeTable.Cols.SUSPECT));
+
+ Crime crime = new Crime(UUID.fromString(uuidString));
+ crime.setTitle(title);
+ crime.setDate(new Date(date));
+ crime.setSolved(isSolved != 0);
+ crime.setSuspect(suspect);
+
+ return crime;
+ }
+}
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeDbSchema.java b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeDbSchema.java
new file mode 100644
index 0000000..88e8d99
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/java/com/bignerdranch/android/criminalintent/database/CrimeDbSchema.java
@@ -0,0 +1,15 @@
+package com.bignerdranch.android.criminalintent.database;
+
+public class CrimeDbSchema {
+ public static final class CrimeTable {
+ public static final String NAME = "crimes";
+
+ public static final class Cols {
+ public static final String UUID = "uuid";
+ public static final String TITLE = "title";
+ public static final String DATE = "date";
+ public static final String SOLVED = "solved";
+ public static final String SUSPECT = "suspect";
+ }
+ }
+}
\ No newline at end of file
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/drawable-hdpi/ic_menu_add.png b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/drawable-hdpi/ic_menu_add.png
new file mode 100644
index 0000000..1ae5b2d
Binary files /dev/null and b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/drawable-hdpi/ic_menu_add.png differ
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/drawable-hdpi/ic_solved.png b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/drawable-hdpi/ic_solved.png
new file mode 100644
index 0000000..e9b9263
Binary files /dev/null and b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/drawable-hdpi/ic_solved.png differ
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/drawable-mdpi/ic_menu_add.png b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/drawable-mdpi/ic_menu_add.png
new file mode 100644
index 0000000..d51f0dd
Binary files /dev/null and b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/drawable-mdpi/ic_menu_add.png differ
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/drawable-mdpi/ic_solved.png b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/drawable-mdpi/ic_solved.png
new file mode 100644
index 0000000..9548d3c
Binary files /dev/null and b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/drawable-mdpi/ic_solved.png differ
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_menu_add.png b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_menu_add.png
new file mode 100644
index 0000000..9ea0eeb
Binary files /dev/null and b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_menu_add.png differ
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_solved.png b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_solved.png
new file mode 100644
index 0000000..6ab438d
Binary files /dev/null and b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/drawable-xhdpi/ic_solved.png differ
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_menu_add.png b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_menu_add.png
new file mode 100644
index 0000000..75f192a
Binary files /dev/null and b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_menu_add.png differ
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_solved.png b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_solved.png
new file mode 100644
index 0000000..ca05191
Binary files /dev/null and b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/drawable-xxhdpi/ic_solved.png differ
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/drawable-xxxhdpi/ic_solved.png b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/drawable-xxxhdpi/ic_solved.png
new file mode 100644
index 0000000..15d3cf4
Binary files /dev/null and b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/drawable-xxxhdpi/ic_solved.png differ
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/layout/activity_crime_pager.xml b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/layout/activity_crime_pager.xml
new file mode 100644
index 0000000..a80c096
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/layout/activity_crime_pager.xml
@@ -0,0 +1,5 @@
+
+
\ No newline at end of file
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/layout/activity_fragment.xml b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/layout/activity_fragment.xml
new file mode 100644
index 0000000..1773143
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/layout/activity_fragment.xml
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/layout/activity_twopane.xml b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/layout/activity_twopane.xml
new file mode 100644
index 0000000..337db3c
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/layout/activity_twopane.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/layout/dialog_date.xml b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/layout/dialog_date.xml
new file mode 100644
index 0000000..c35ec4c
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/layout/dialog_date.xml
@@ -0,0 +1,6 @@
+
+
\ No newline at end of file
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/layout/fragment_crime.xml b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/layout/fragment_crime.xml
new file mode 100644
index 0000000..aaf5122
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/layout/fragment_crime.xml
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml
new file mode 100644
index 0000000..4d23b7c
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/layout/fragment_crime_list.xml
@@ -0,0 +1,5 @@
+
+
\ No newline at end of file
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/layout/list_item_crime.xml b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/layout/list_item_crime.xml
new file mode 100644
index 0000000..e149593
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/layout/list_item_crime.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/menu/fragment_crime_list.xml b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/menu/fragment_crime_list.xml
new file mode 100644
index 0000000..de478c1
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/menu/fragment_crime_list.xml
@@ -0,0 +1,14 @@
+
+
\ No newline at end of file
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/values-sw600dp/refs.xml b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/values-sw600dp/refs.xml
new file mode 100644
index 0000000..8c92508
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/values-sw600dp/refs.xml
@@ -0,0 +1,6 @@
+
+
+
+ @layout/activity_twopane
+
+
\ No newline at end of file
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/values/colors.xml b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/values/dimens.xml b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/values/refs.xml b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/values/refs.xml
new file mode 100644
index 0000000..70e38b1
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/values/refs.xml
@@ -0,0 +1,6 @@
+
+
+
+ @layout/activity_fragment
+
+
\ No newline at end of file
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/values/strings.xml b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..2d757e3
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/values/strings.xml
@@ -0,0 +1,23 @@
+
+ CriminalIntent
+ Enter a title for the crime.
+ Title
+ Details
+ Solved
+ Date of crime:
+ New Crime
+ Show Subtitle
+ Hide Subtitle
+ %1$d crimes
+ Choose Suspect
+ Send Crime Report
+ %1$s!
+ The crime was discovered on %2$s. %3$s, and %4$s
+
+ The case is solved
+ The case is not solved
+ there is no suspect.
+ the suspect is %s.
+ CriminalIntent Crime Report
+ Send crime report via
+
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/values/styles.xml b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/xml/files.xml b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/xml/files.xml
new file mode 100644
index 0000000..c27251b
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/main/res/xml/files.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java b/Book Files/17_FragmentComposition/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java
new file mode 100644
index 0000000..e0e2773
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/app/src/test/java/com/bignerdranch/android/criminalintent/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.criminalintent;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/build.gradle b/Book Files/17_FragmentComposition/CriminalIntent/build.gradle
new file mode 100644
index 0000000..1ea4bd0
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/gradle.properties b/Book Files/17_FragmentComposition/CriminalIntent/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/gradle/wrapper/gradle-wrapper.jar b/Book Files/17_FragmentComposition/CriminalIntent/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/Book Files/17_FragmentComposition/CriminalIntent/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/gradle/wrapper/gradle-wrapper.properties b/Book Files/17_FragmentComposition/CriminalIntent/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a82392d
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/gradlew b/Book Files/17_FragmentComposition/CriminalIntent/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/gradlew.bat b/Book Files/17_FragmentComposition/CriminalIntent/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/17_FragmentComposition/CriminalIntent/settings.gradle b/Book Files/17_FragmentComposition/CriminalIntent/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/17_FragmentComposition/CriminalIntent/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/17_FragmentComposition/LICENSE.txt b/Book Files/17_FragmentComposition/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Book Files/17_FragmentComposition/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Book Files/18_Localization/CriminalIntent/.gitignore b/Book Files/18_Localization/CriminalIntent/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/Book Files/18_Localization/CriminalIntent/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Book Files/18_Localization/CriminalIntent/.idea/compiler.xml b/Book Files/18_Localization/CriminalIntent/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/Book Files/18_Localization/CriminalIntent/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/18_Localization/CriminalIntent/.idea/copyright/profiles_settings.xml b/Book Files/18_Localization/CriminalIntent/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/Book Files/18_Localization/CriminalIntent/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Book Files/18_Localization/CriminalIntent/.idea/encodings.xml b/Book Files/18_Localization/CriminalIntent/.idea/encodings.xml
new file mode 100644
index 0000000..97626ba
--- /dev/null
+++ b/Book Files/18_Localization/CriminalIntent/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/18_Localization/CriminalIntent/.idea/gradle.xml b/Book Files/18_Localization/CriminalIntent/.idea/gradle.xml
new file mode 100644
index 0000000..7ac24c7
--- /dev/null
+++ b/Book Files/18_Localization/CriminalIntent/.idea/gradle.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/18_Localization/CriminalIntent/.idea/misc.xml b/Book Files/18_Localization/CriminalIntent/.idea/misc.xml
new file mode 100644
index 0000000..5d19981
--- /dev/null
+++ b/Book Files/18_Localization/CriminalIntent/.idea/misc.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/18_Localization/CriminalIntent/.idea/modules.xml b/Book Files/18_Localization/CriminalIntent/.idea/modules.xml
new file mode 100644
index 0000000..37be0dd
--- /dev/null
+++ b/Book Files/18_Localization/CriminalIntent/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/18_Localization/CriminalIntent/.idea/runConfigurations.xml b/Book Files/18_Localization/CriminalIntent/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/Book Files/18_Localization/CriminalIntent/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/22_Themes/BeatBox/.idea/misc.xml b/Book Files/22_Themes/BeatBox/.idea/misc.xml
new file mode 100644
index 0000000..1d77643
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/.idea/misc.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/22_Themes/BeatBox/.idea/modules.xml b/Book Files/22_Themes/BeatBox/.idea/modules.xml
new file mode 100644
index 0000000..7a7fe8a
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/22_Themes/BeatBox/.idea/runConfigurations.xml b/Book Files/22_Themes/BeatBox/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/22_Themes/BeatBox/app/.gitignore b/Book Files/22_Themes/BeatBox/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/22_Themes/BeatBox/app/build.gradle b/Book Files/22_Themes/BeatBox/app/build.gradle
new file mode 100644
index 0000000..4f114b0
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/app/build.gradle
@@ -0,0 +1,35 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ applicationId "com.bignerdranch.android.beatbox"
+ minSdkVersion 19
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+ dataBinding {
+ enabled = true
+ }
+}
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ testCompile 'junit:junit:4.12'
+ compile 'com.android.support:recyclerview-v7:25.3.0'
+ testCompile 'org.mockito:mockito-core:2.2.1'
+ testCompile 'org.hamcrest:hamcrest-junit:2.0.0.0'
+}
diff --git a/Book Files/22_Themes/BeatBox/app/proguard-rules.pro b/Book Files/22_Themes/BeatBox/app/proguard-rules.pro
new file mode 100644
index 0000000..cf5610c
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/bphillips/devtools/android-sdk-macosx/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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 *;
+#}
diff --git a/Book Files/22_Themes/BeatBox/app/src/androidTest/java/com/bignerdranch/android/beatbox/ExampleInstrumentedTest.java b/Book Files/22_Themes/BeatBox/app/src/androidTest/java/com/bignerdranch/android/beatbox/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..ed00282
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/app/src/androidTest/java/com/bignerdranch/android/beatbox/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.beatbox;
+
+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.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.bignerdranch.android.beatbox", appContext.getPackageName());
+ }
+}
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/AndroidManifest.xml b/Book Files/22_Themes/BeatBox/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..844c219
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/app/src/main/AndroidManifest.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/65_cjipie.wav b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/65_cjipie.wav
new file mode 100644
index 0000000..2b08a2f
Binary files /dev/null and b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/65_cjipie.wav differ
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/66_indios.wav b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/66_indios.wav
new file mode 100644
index 0000000..c6c0d2a
Binary files /dev/null and b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/66_indios.wav differ
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/67_indios2.wav b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/67_indios2.wav
new file mode 100644
index 0000000..c3e8008
Binary files /dev/null and b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/67_indios2.wav differ
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/68_indios3.wav b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/68_indios3.wav
new file mode 100644
index 0000000..71e08f2
Binary files /dev/null and b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/68_indios3.wav differ
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/69_ohm-loko.wav b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/69_ohm-loko.wav
new file mode 100644
index 0000000..2759e35
Binary files /dev/null and b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/69_ohm-loko.wav differ
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/70_eh.wav b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/70_eh.wav
new file mode 100644
index 0000000..903d367
Binary files /dev/null and b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/70_eh.wav differ
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/71_hruuhb.wav b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/71_hruuhb.wav
new file mode 100644
index 0000000..2987f56
Binary files /dev/null and b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/71_hruuhb.wav differ
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/72_houb.wav b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/72_houb.wav
new file mode 100644
index 0000000..d39b69d
Binary files /dev/null and b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/72_houb.wav differ
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/73_houu.wav b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/73_houu.wav
new file mode 100644
index 0000000..b59a8fa
Binary files /dev/null and b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/73_houu.wav differ
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/74_jah.wav b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/74_jah.wav
new file mode 100644
index 0000000..9d35d6c
Binary files /dev/null and b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/74_jah.wav differ
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/75_jhuee.wav b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/75_jhuee.wav
new file mode 100644
index 0000000..84adb4f
Binary files /dev/null and b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/75_jhuee.wav differ
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/76_joooaah.wav b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/76_joooaah.wav
new file mode 100644
index 0000000..6ccfcf8
Binary files /dev/null and b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/76_joooaah.wav differ
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/77_juob.wav b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/77_juob.wav
new file mode 100644
index 0000000..4d36650
Binary files /dev/null and b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/77_juob.wav differ
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/78_jueb.wav b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/78_jueb.wav
new file mode 100644
index 0000000..054d26c
Binary files /dev/null and b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/78_jueb.wav differ
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/79_long-scream.wav b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/79_long-scream.wav
new file mode 100644
index 0000000..1d36413
Binary files /dev/null and b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/79_long-scream.wav differ
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/80_oaaaahmmm.wav b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/80_oaaaahmmm.wav
new file mode 100644
index 0000000..3ce4ee2
Binary files /dev/null and b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/80_oaaaahmmm.wav differ
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/81_uehea.wav b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/81_uehea.wav
new file mode 100644
index 0000000..06c3f70
Binary files /dev/null and b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/81_uehea.wav differ
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/82_uhraa.wav b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/82_uhraa.wav
new file mode 100644
index 0000000..09cdfda
Binary files /dev/null and b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/82_uhraa.wav differ
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/83_uoh.wav b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/83_uoh.wav
new file mode 100644
index 0000000..d8bd922
Binary files /dev/null and b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/83_uoh.wav differ
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/84_uueh.wav b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/84_uueh.wav
new file mode 100644
index 0000000..e7dbfc0
Binary files /dev/null and b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/84_uueh.wav differ
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/85_jeeh.wav b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/85_jeeh.wav
new file mode 100644
index 0000000..ecc8560
Binary files /dev/null and b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/85_jeeh.wav differ
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/86_oa-h.wav b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/86_oa-h.wav
new file mode 100644
index 0000000..d806fb0
Binary files /dev/null and b/Book Files/22_Themes/BeatBox/app/src/main/assets/sample_sounds/86_oa-h.wav differ
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/BeatBox.java b/Book Files/22_Themes/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/BeatBox.java
new file mode 100644
index 0000000..2a02c97
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/BeatBox.java
@@ -0,0 +1,76 @@
+package com.bignerdranch.android.beatbox;
+
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.AssetManager;
+import android.media.AudioManager;
+import android.media.SoundPool;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class BeatBox {
+ private static final String TAG = "BeatBox";
+
+ private static final String SOUNDS_FOLDER = "sample_sounds";
+ private static final int MAX_SOUNDS = 5;
+
+ private AssetManager mAssets;
+ private List mSounds = new ArrayList<>();
+ private SoundPool mSoundPool;
+
+ public BeatBox(Context context) {
+ mAssets = context.getAssets();
+ // This old constructor is deprecated, but we need it for
+ // compatibility.
+ //noinspection deprecation
+ mSoundPool = new SoundPool(MAX_SOUNDS, AudioManager.STREAM_MUSIC, 0);
+ loadSounds();
+ }
+
+ public void play(Sound sound) {
+ Integer soundId = sound.getSoundId();
+ if (soundId == null) {
+ return;
+ }
+ mSoundPool.play(soundId, 1.0f, 1.0f, 1, 0, 1.0f);
+ }
+
+ public void release() {
+ mSoundPool.release();
+ }
+
+ private void loadSounds() {
+ String[] soundNames;
+ try {
+ soundNames = mAssets.list(SOUNDS_FOLDER);
+ Log.i(TAG, "Found " + soundNames.length + " sounds");
+ } catch (IOException ioe) {
+ Log.e(TAG, "Could not list assets", ioe);
+ return;
+ }
+
+ for (String filename : soundNames) {
+ try {
+ String assetPath = SOUNDS_FOLDER + "/" + filename;
+ Sound sound = new Sound(assetPath);
+ load(sound);
+ mSounds.add(sound);
+ } catch (IOException ioe) {
+ Log.e(TAG, "Could not load sound " + filename, ioe);
+ }
+ }
+ }
+
+ private void load(Sound sound) throws IOException {
+ AssetFileDescriptor assetFd = mAssets.openFd(sound.getAssetPath());
+ int soundId = mSoundPool.load(assetFd, 1);
+ sound.setSoundId(soundId);
+ }
+
+ public List getSounds() {
+ return mSounds;
+ }
+}
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/BeatBoxActivity.java b/Book Files/22_Themes/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/BeatBoxActivity.java
new file mode 100644
index 0000000..1a09b09
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/BeatBoxActivity.java
@@ -0,0 +1,12 @@
+package com.bignerdranch.android.beatbox;
+
+import android.support.v4.app.Fragment;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+
+public class BeatBoxActivity extends SingleFragmentActivity {
+ @Override
+ protected Fragment createFragment() {
+ return BeatBoxFragment.newInstance();
+ }
+}
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/BeatBoxFragment.java b/Book Files/22_Themes/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/BeatBoxFragment.java
new file mode 100644
index 0000000..1a6adc4
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/BeatBoxFragment.java
@@ -0,0 +1,94 @@
+package com.bignerdranch.android.beatbox;
+
+import android.databinding.DataBindingUtil;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.bignerdranch.android.beatbox.databinding.FragmentBeatBoxBinding;
+import com.bignerdranch.android.beatbox.databinding.ListItemSoundBinding;
+
+import java.util.List;
+
+
+public class BeatBoxFragment extends Fragment {
+
+ private BeatBox mBeatBox;
+
+ public static BeatBoxFragment newInstance() {
+ return new BeatBoxFragment();
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setRetainInstance(true);
+
+ mBeatBox = new BeatBox(getActivity());
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ FragmentBeatBoxBinding binding = DataBindingUtil
+ .inflate(inflater, R.layout.fragment_beat_box, container, false);
+
+ binding.recyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 3));
+ binding.recyclerView.setAdapter(new SoundAdapter(mBeatBox.getSounds()));
+
+ return binding.getRoot();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mBeatBox.release();
+ }
+
+ private class SoundHolder extends RecyclerView.ViewHolder {
+ private ListItemSoundBinding mBinding;
+
+ private SoundHolder(ListItemSoundBinding binding) {
+ super(binding.getRoot());
+ mBinding = binding;
+ mBinding.setViewModel(new SoundViewModel(mBeatBox));
+ }
+
+ public void bind(Sound sound) {
+ mBinding.getViewModel().setSound(sound);
+ mBinding.executePendingBindings();
+ }
+ }
+
+ private class SoundAdapter extends RecyclerView.Adapter {
+ private List mSounds;
+
+ public SoundAdapter(List sounds) {
+ mSounds = sounds;
+ }
+
+ @Override
+ public SoundHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ LayoutInflater inflater = LayoutInflater.from(getActivity());
+ ListItemSoundBinding binding = DataBindingUtil
+ .inflate(inflater, R.layout.list_item_sound, parent, false);
+ return new SoundHolder(binding);
+ }
+
+ @Override
+ public void onBindViewHolder(SoundHolder holder, int position) {
+ Sound sound = mSounds.get(position);
+ holder.bind(sound);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mSounds.size();
+ }
+ }
+}
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/SingleFragmentActivity.java b/Book Files/22_Themes/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/SingleFragmentActivity.java
new file mode 100644
index 0000000..5d5993c
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/SingleFragmentActivity.java
@@ -0,0 +1,30 @@
+package com.bignerdranch.android.beatbox;
+
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.AppCompatActivity;
+
+public abstract class SingleFragmentActivity extends AppCompatActivity {
+ protected abstract Fragment createFragment();
+
+ protected int getLayoutResId() {
+ return R.layout.activity_fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(getLayoutResId());
+ FragmentManager manager = getSupportFragmentManager();
+ Fragment fragment = manager.findFragmentById(R.id.fragment_container);
+
+ if (fragment == null) {
+ fragment = createFragment();
+ manager.beginTransaction()
+ .add(R.id.fragment_container, fragment)
+ .commit();
+ }
+ }
+}
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/Sound.java b/Book Files/22_Themes/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/Sound.java
new file mode 100644
index 0000000..531733e
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/Sound.java
@@ -0,0 +1,30 @@
+package com.bignerdranch.android.beatbox;
+
+public class Sound {
+ private String mAssetPath;
+ private String mName;
+ private Integer mSoundId;
+
+ public Sound(String assetPath) {
+ mAssetPath = assetPath;
+ String[] components = assetPath.split("/");
+ String filename = components[components.length - 1];
+ mName = filename.replace(".wav", "");
+ }
+
+ public String getAssetPath() {
+ return mAssetPath;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public Integer getSoundId() {
+ return mSoundId;
+ }
+
+ public void setSoundId(Integer soundId) {
+ mSoundId = soundId;
+ }
+}
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/SoundViewModel.java b/Book Files/22_Themes/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/SoundViewModel.java
new file mode 100644
index 0000000..56a6d2c
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/SoundViewModel.java
@@ -0,0 +1,32 @@
+package com.bignerdranch.android.beatbox;
+
+import android.databinding.BaseObservable;
+import android.databinding.Bindable;
+import android.util.Log;
+
+public class SoundViewModel extends BaseObservable {
+ private Sound mSound;
+ private BeatBox mBeatBox;
+
+ public SoundViewModel(BeatBox beatBox) {
+ mBeatBox = beatBox;
+ }
+
+ @Bindable
+ public String getTitle() {
+ return mSound.getName();
+ }
+
+ public Sound getSound() {
+ return mSound;
+ }
+
+ public void setSound(Sound sound) {
+ mSound = sound;
+ notifyChange();
+ }
+
+ public void onButtonClicked() {
+ mBeatBox.play(mSound);
+ }
+}
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/res/layout/activity_fragment.xml b/Book Files/22_Themes/BeatBox/app/src/main/res/layout/activity_fragment.xml
new file mode 100644
index 0000000..347d1d9
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/app/src/main/res/layout/activity_fragment.xml
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/res/layout/fragment_beat_box.xml b/Book Files/22_Themes/BeatBox/app/src/main/res/layout/fragment_beat_box.xml
new file mode 100644
index 0000000..eb270f4
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/app/src/main/res/layout/fragment_beat_box.xml
@@ -0,0 +1,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/res/layout/list_item_sound.xml b/Book Files/22_Themes/BeatBox/app/src/main/res/layout/list_item_sound.xml
new file mode 100644
index 0000000..5de8fef
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/app/src/main/res/layout/list_item_sound.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Book Files/22_Themes/BeatBox/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/Book Files/22_Themes/BeatBox/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Book Files/22_Themes/BeatBox/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/Book Files/22_Themes/BeatBox/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Book Files/22_Themes/BeatBox/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/Book Files/22_Themes/BeatBox/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Book Files/22_Themes/BeatBox/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/Book Files/22_Themes/BeatBox/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Book Files/22_Themes/BeatBox/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/Book Files/22_Themes/BeatBox/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/res/values-w820dp/dimens.xml b/Book Files/22_Themes/BeatBox/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/res/values/colors.xml b/Book Files/22_Themes/BeatBox/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..e8ca4d6
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/app/src/main/res/values/colors.xml
@@ -0,0 +1,12 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
+ #F44336
+ #C3352B
+ #607D8B
+ #0083BF
+ #005A8A
+
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/res/values/dimens.xml b/Book Files/22_Themes/BeatBox/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/res/values/strings.xml b/Book Files/22_Themes/BeatBox/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..24ab35a
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ BeatBox
+
diff --git a/Book Files/22_Themes/BeatBox/app/src/main/res/values/styles.xml b/Book Files/22_Themes/BeatBox/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..4691256
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
diff --git a/Book Files/22_Themes/BeatBox/app/src/test/java/com/bignerdranch/android/beatbox/ExampleUnitTest.java b/Book Files/22_Themes/BeatBox/app/src/test/java/com/bignerdranch/android/beatbox/ExampleUnitTest.java
new file mode 100644
index 0000000..a8bc989
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/app/src/test/java/com/bignerdranch/android/beatbox/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.beatbox;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/22_Themes/BeatBox/app/src/test/java/com/bignerdranch/android/beatbox/SoundViewModelTest.java b/Book Files/22_Themes/BeatBox/app/src/test/java/com/bignerdranch/android/beatbox/SoundViewModelTest.java
new file mode 100644
index 0000000..e38ca2a
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/app/src/test/java/com/bignerdranch/android/beatbox/SoundViewModelTest.java
@@ -0,0 +1,34 @@
+package com.bignerdranch.android.beatbox;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class SoundViewModelTest {
+ private BeatBox mBeatBox;
+ private Sound mSound;
+ private SoundViewModel mSubject;
+
+ @Before
+ public void setUp() throws Exception {
+ mBeatBox = mock(BeatBox.class);
+ mSound = new Sound("assetPath");
+ mSubject = new SoundViewModel(mBeatBox);
+ mSubject.setSound(mSound);
+ }
+
+ @Test
+ public void exposesSoundNameAsTitle() {
+ assertThat(mSubject.getTitle(), is(mSound.getName()));
+ }
+
+ @Test
+ public void callsBeatBoxPlayOnButtonClicked() {
+ mSubject.onButtonClicked();
+ verify(mBeatBox).play(mSound);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/22_Themes/BeatBox/build.gradle b/Book Files/22_Themes/BeatBox/build.gradle
new file mode 100644
index 0000000..1ea4bd0
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Book Files/22_Themes/BeatBox/gradle.properties b/Book Files/22_Themes/BeatBox/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/Book Files/22_Themes/BeatBox/gradle/wrapper/gradle-wrapper.jar b/Book Files/22_Themes/BeatBox/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/Book Files/22_Themes/BeatBox/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/22_Themes/BeatBox/gradle/wrapper/gradle-wrapper.properties b/Book Files/22_Themes/BeatBox/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a82392d
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/22_Themes/BeatBox/gradlew b/Book Files/22_Themes/BeatBox/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/22_Themes/BeatBox/gradlew.bat b/Book Files/22_Themes/BeatBox/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/22_Themes/BeatBox/settings.gradle b/Book Files/22_Themes/BeatBox/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/22_Themes/BeatBox/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/22_Themes/LICENSE.txt b/Book Files/22_Themes/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Book Files/22_Themes/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Book Files/22_Themes/README.md b/Book Files/22_Themes/README.md
new file mode 100644
index 0000000..00d5165
--- /dev/null
+++ b/Book Files/22_Themes/README.md
@@ -0,0 +1,3 @@
+# Credit goes where credit is due
+
+Bill, Chris, and Brian were responsible for this code. The sounds, though? The sounds in BeatBox/app/src/main/assets/rasslin? We got them from plagasul, at [http://www.freesound.org/people/plagasul/packs/3/](http://www.freesound.org/people/plagasul/packs/3/).
diff --git a/Book Files/23_XMLDrawables/.DS_Store b/Book Files/23_XMLDrawables/.DS_Store
new file mode 100644
index 0000000..5008ddf
Binary files /dev/null and b/Book Files/23_XMLDrawables/.DS_Store differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/.gitignore b/Book Files/23_XMLDrawables/BeatBox/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Book Files/23_XMLDrawables/BeatBox/.idea/compiler.xml b/Book Files/23_XMLDrawables/BeatBox/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/23_XMLDrawables/BeatBox/.idea/copyright/profiles_settings.xml b/Book Files/23_XMLDrawables/BeatBox/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Book Files/23_XMLDrawables/BeatBox/.idea/encodings.xml b/Book Files/23_XMLDrawables/BeatBox/.idea/encodings.xml
new file mode 100644
index 0000000..97626ba
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/23_XMLDrawables/BeatBox/.idea/gradle.xml b/Book Files/23_XMLDrawables/BeatBox/.idea/gradle.xml
new file mode 100644
index 0000000..4819d5a
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/.idea/gradle.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/23_XMLDrawables/BeatBox/.idea/misc.xml b/Book Files/23_XMLDrawables/BeatBox/.idea/misc.xml
new file mode 100644
index 0000000..1d9d626
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/.idea/misc.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/23_XMLDrawables/BeatBox/.idea/modules.xml b/Book Files/23_XMLDrawables/BeatBox/.idea/modules.xml
new file mode 100644
index 0000000..7a7fe8a
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/23_XMLDrawables/BeatBox/.idea/runConfigurations.xml b/Book Files/23_XMLDrawables/BeatBox/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/.gitignore b/Book Files/23_XMLDrawables/BeatBox/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/build.gradle b/Book Files/23_XMLDrawables/BeatBox/app/build.gradle
new file mode 100644
index 0000000..4f114b0
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/app/build.gradle
@@ -0,0 +1,35 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ applicationId "com.bignerdranch.android.beatbox"
+ minSdkVersion 19
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+ dataBinding {
+ enabled = true
+ }
+}
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ testCompile 'junit:junit:4.12'
+ compile 'com.android.support:recyclerview-v7:25.3.0'
+ testCompile 'org.mockito:mockito-core:2.2.1'
+ testCompile 'org.hamcrest:hamcrest-junit:2.0.0.0'
+}
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/proguard-rules.pro b/Book Files/23_XMLDrawables/BeatBox/app/proguard-rules.pro
new file mode 100644
index 0000000..cf5610c
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/bphillips/devtools/android-sdk-macosx/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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 *;
+#}
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/androidTest/java/com/bignerdranch/android/beatbox/ExampleInstrumentedTest.java b/Book Files/23_XMLDrawables/BeatBox/app/src/androidTest/java/com/bignerdranch/android/beatbox/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..ed00282
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/app/src/androidTest/java/com/bignerdranch/android/beatbox/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.beatbox;
+
+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.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.bignerdranch.android.beatbox", appContext.getPackageName());
+ }
+}
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/AndroidManifest.xml b/Book Files/23_XMLDrawables/BeatBox/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..844c219
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/app/src/main/AndroidManifest.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/65_cjipie.wav b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/65_cjipie.wav
new file mode 100644
index 0000000..2b08a2f
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/65_cjipie.wav differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/66_indios.wav b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/66_indios.wav
new file mode 100644
index 0000000..c6c0d2a
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/66_indios.wav differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/67_indios2.wav b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/67_indios2.wav
new file mode 100644
index 0000000..c3e8008
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/67_indios2.wav differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/68_indios3.wav b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/68_indios3.wav
new file mode 100644
index 0000000..71e08f2
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/68_indios3.wav differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/69_ohm-loko.wav b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/69_ohm-loko.wav
new file mode 100644
index 0000000..2759e35
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/69_ohm-loko.wav differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/70_eh.wav b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/70_eh.wav
new file mode 100644
index 0000000..903d367
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/70_eh.wav differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/71_hruuhb.wav b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/71_hruuhb.wav
new file mode 100644
index 0000000..2987f56
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/71_hruuhb.wav differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/72_houb.wav b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/72_houb.wav
new file mode 100644
index 0000000..d39b69d
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/72_houb.wav differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/73_houu.wav b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/73_houu.wav
new file mode 100644
index 0000000..b59a8fa
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/73_houu.wav differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/74_jah.wav b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/74_jah.wav
new file mode 100644
index 0000000..9d35d6c
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/74_jah.wav differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/75_jhuee.wav b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/75_jhuee.wav
new file mode 100644
index 0000000..84adb4f
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/75_jhuee.wav differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/76_joooaah.wav b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/76_joooaah.wav
new file mode 100644
index 0000000..6ccfcf8
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/76_joooaah.wav differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/77_juob.wav b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/77_juob.wav
new file mode 100644
index 0000000..4d36650
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/77_juob.wav differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/78_jueb.wav b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/78_jueb.wav
new file mode 100644
index 0000000..054d26c
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/78_jueb.wav differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/79_long-scream.wav b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/79_long-scream.wav
new file mode 100644
index 0000000..1d36413
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/79_long-scream.wav differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/80_oaaaahmmm.wav b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/80_oaaaahmmm.wav
new file mode 100644
index 0000000..3ce4ee2
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/80_oaaaahmmm.wav differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/81_uehea.wav b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/81_uehea.wav
new file mode 100644
index 0000000..06c3f70
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/81_uehea.wav differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/82_uhraa.wav b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/82_uhraa.wav
new file mode 100644
index 0000000..09cdfda
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/82_uhraa.wav differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/83_uoh.wav b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/83_uoh.wav
new file mode 100644
index 0000000..d8bd922
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/83_uoh.wav differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/84_uueh.wav b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/84_uueh.wav
new file mode 100644
index 0000000..e7dbfc0
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/84_uueh.wav differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/85_jeeh.wav b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/85_jeeh.wav
new file mode 100644
index 0000000..ecc8560
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/85_jeeh.wav differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/86_oa-h.wav b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/86_oa-h.wav
new file mode 100644
index 0000000..d806fb0
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/app/src/main/assets/sample_sounds/86_oa-h.wav differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/BeatBox.java b/Book Files/23_XMLDrawables/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/BeatBox.java
new file mode 100644
index 0000000..2a02c97
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/BeatBox.java
@@ -0,0 +1,76 @@
+package com.bignerdranch.android.beatbox;
+
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.AssetManager;
+import android.media.AudioManager;
+import android.media.SoundPool;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class BeatBox {
+ private static final String TAG = "BeatBox";
+
+ private static final String SOUNDS_FOLDER = "sample_sounds";
+ private static final int MAX_SOUNDS = 5;
+
+ private AssetManager mAssets;
+ private List mSounds = new ArrayList<>();
+ private SoundPool mSoundPool;
+
+ public BeatBox(Context context) {
+ mAssets = context.getAssets();
+ // This old constructor is deprecated, but we need it for
+ // compatibility.
+ //noinspection deprecation
+ mSoundPool = new SoundPool(MAX_SOUNDS, AudioManager.STREAM_MUSIC, 0);
+ loadSounds();
+ }
+
+ public void play(Sound sound) {
+ Integer soundId = sound.getSoundId();
+ if (soundId == null) {
+ return;
+ }
+ mSoundPool.play(soundId, 1.0f, 1.0f, 1, 0, 1.0f);
+ }
+
+ public void release() {
+ mSoundPool.release();
+ }
+
+ private void loadSounds() {
+ String[] soundNames;
+ try {
+ soundNames = mAssets.list(SOUNDS_FOLDER);
+ Log.i(TAG, "Found " + soundNames.length + " sounds");
+ } catch (IOException ioe) {
+ Log.e(TAG, "Could not list assets", ioe);
+ return;
+ }
+
+ for (String filename : soundNames) {
+ try {
+ String assetPath = SOUNDS_FOLDER + "/" + filename;
+ Sound sound = new Sound(assetPath);
+ load(sound);
+ mSounds.add(sound);
+ } catch (IOException ioe) {
+ Log.e(TAG, "Could not load sound " + filename, ioe);
+ }
+ }
+ }
+
+ private void load(Sound sound) throws IOException {
+ AssetFileDescriptor assetFd = mAssets.openFd(sound.getAssetPath());
+ int soundId = mSoundPool.load(assetFd, 1);
+ sound.setSoundId(soundId);
+ }
+
+ public List getSounds() {
+ return mSounds;
+ }
+}
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/BeatBoxActivity.java b/Book Files/23_XMLDrawables/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/BeatBoxActivity.java
new file mode 100644
index 0000000..1a09b09
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/BeatBoxActivity.java
@@ -0,0 +1,12 @@
+package com.bignerdranch.android.beatbox;
+
+import android.support.v4.app.Fragment;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+
+public class BeatBoxActivity extends SingleFragmentActivity {
+ @Override
+ protected Fragment createFragment() {
+ return BeatBoxFragment.newInstance();
+ }
+}
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/BeatBoxFragment.java b/Book Files/23_XMLDrawables/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/BeatBoxFragment.java
new file mode 100644
index 0000000..1a6adc4
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/BeatBoxFragment.java
@@ -0,0 +1,94 @@
+package com.bignerdranch.android.beatbox;
+
+import android.databinding.DataBindingUtil;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.bignerdranch.android.beatbox.databinding.FragmentBeatBoxBinding;
+import com.bignerdranch.android.beatbox.databinding.ListItemSoundBinding;
+
+import java.util.List;
+
+
+public class BeatBoxFragment extends Fragment {
+
+ private BeatBox mBeatBox;
+
+ public static BeatBoxFragment newInstance() {
+ return new BeatBoxFragment();
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setRetainInstance(true);
+
+ mBeatBox = new BeatBox(getActivity());
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ FragmentBeatBoxBinding binding = DataBindingUtil
+ .inflate(inflater, R.layout.fragment_beat_box, container, false);
+
+ binding.recyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 3));
+ binding.recyclerView.setAdapter(new SoundAdapter(mBeatBox.getSounds()));
+
+ return binding.getRoot();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mBeatBox.release();
+ }
+
+ private class SoundHolder extends RecyclerView.ViewHolder {
+ private ListItemSoundBinding mBinding;
+
+ private SoundHolder(ListItemSoundBinding binding) {
+ super(binding.getRoot());
+ mBinding = binding;
+ mBinding.setViewModel(new SoundViewModel(mBeatBox));
+ }
+
+ public void bind(Sound sound) {
+ mBinding.getViewModel().setSound(sound);
+ mBinding.executePendingBindings();
+ }
+ }
+
+ private class SoundAdapter extends RecyclerView.Adapter {
+ private List mSounds;
+
+ public SoundAdapter(List sounds) {
+ mSounds = sounds;
+ }
+
+ @Override
+ public SoundHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ LayoutInflater inflater = LayoutInflater.from(getActivity());
+ ListItemSoundBinding binding = DataBindingUtil
+ .inflate(inflater, R.layout.list_item_sound, parent, false);
+ return new SoundHolder(binding);
+ }
+
+ @Override
+ public void onBindViewHolder(SoundHolder holder, int position) {
+ Sound sound = mSounds.get(position);
+ holder.bind(sound);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mSounds.size();
+ }
+ }
+}
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/SingleFragmentActivity.java b/Book Files/23_XMLDrawables/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/SingleFragmentActivity.java
new file mode 100644
index 0000000..5d5993c
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/SingleFragmentActivity.java
@@ -0,0 +1,30 @@
+package com.bignerdranch.android.beatbox;
+
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.AppCompatActivity;
+
+public abstract class SingleFragmentActivity extends AppCompatActivity {
+ protected abstract Fragment createFragment();
+
+ protected int getLayoutResId() {
+ return R.layout.activity_fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(getLayoutResId());
+ FragmentManager manager = getSupportFragmentManager();
+ Fragment fragment = manager.findFragmentById(R.id.fragment_container);
+
+ if (fragment == null) {
+ fragment = createFragment();
+ manager.beginTransaction()
+ .add(R.id.fragment_container, fragment)
+ .commit();
+ }
+ }
+}
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/Sound.java b/Book Files/23_XMLDrawables/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/Sound.java
new file mode 100644
index 0000000..531733e
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/Sound.java
@@ -0,0 +1,30 @@
+package com.bignerdranch.android.beatbox;
+
+public class Sound {
+ private String mAssetPath;
+ private String mName;
+ private Integer mSoundId;
+
+ public Sound(String assetPath) {
+ mAssetPath = assetPath;
+ String[] components = assetPath.split("/");
+ String filename = components[components.length - 1];
+ mName = filename.replace(".wav", "");
+ }
+
+ public String getAssetPath() {
+ return mAssetPath;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public Integer getSoundId() {
+ return mSoundId;
+ }
+
+ public void setSoundId(Integer soundId) {
+ mSoundId = soundId;
+ }
+}
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/SoundViewModel.java b/Book Files/23_XMLDrawables/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/SoundViewModel.java
new file mode 100644
index 0000000..56a6d2c
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/SoundViewModel.java
@@ -0,0 +1,32 @@
+package com.bignerdranch.android.beatbox;
+
+import android.databinding.BaseObservable;
+import android.databinding.Bindable;
+import android.util.Log;
+
+public class SoundViewModel extends BaseObservable {
+ private Sound mSound;
+ private BeatBox mBeatBox;
+
+ public SoundViewModel(BeatBox beatBox) {
+ mBeatBox = beatBox;
+ }
+
+ @Bindable
+ public String getTitle() {
+ return mSound.getName();
+ }
+
+ public Sound getSound() {
+ return mSound;
+ }
+
+ public void setSound(Sound sound) {
+ mSound = sound;
+ notifyChange();
+ }
+
+ public void onButtonClicked() {
+ mBeatBox.play(mSound);
+ }
+}
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/drawable-xxhdpi/ic_button_beat_box_default.png b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/drawable-xxhdpi/ic_button_beat_box_default.png
new file mode 100644
index 0000000..ce5ecee
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/drawable-xxhdpi/ic_button_beat_box_default.png differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/drawable-xxhdpi/ic_button_beat_box_pressed.png b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/drawable-xxhdpi/ic_button_beat_box_pressed.png
new file mode 100644
index 0000000..bbbacfb
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/drawable-xxhdpi/ic_button_beat_box_pressed.png differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/drawable/button_beat_box.xml b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/drawable/button_beat_box.xml
new file mode 100644
index 0000000..5db2f1f
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/drawable/button_beat_box.xml
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/drawable/button_beat_box_normal.xml b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/drawable/button_beat_box_normal.xml
new file mode 100644
index 0000000..928e1f2
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/drawable/button_beat_box_normal.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/drawable/button_beat_box_pressed.xml b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/drawable/button_beat_box_pressed.xml
new file mode 100644
index 0000000..40f5805
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/drawable/button_beat_box_pressed.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/layout/activity_fragment.xml b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/layout/activity_fragment.xml
new file mode 100644
index 0000000..347d1d9
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/layout/activity_fragment.xml
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/layout/fragment_beat_box.xml b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/layout/fragment_beat_box.xml
new file mode 100644
index 0000000..eb270f4
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/layout/fragment_beat_box.xml
@@ -0,0 +1,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/layout/list_item_sound.xml b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/layout/list_item_sound.xml
new file mode 100644
index 0000000..ecbb107
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/layout/list_item_sound.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/values-w820dp/dimens.xml b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/values/colors.xml b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..e8ca4d6
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/values/colors.xml
@@ -0,0 +1,12 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
+ #F44336
+ #C3352B
+ #607D8B
+ #0083BF
+ #005A8A
+
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/values/dimens.xml b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/values/strings.xml b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..24ab35a
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ BeatBox
+
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/values/styles.xml b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..edc50e1
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/test/java/com/bignerdranch/android/beatbox/ExampleUnitTest.java b/Book Files/23_XMLDrawables/BeatBox/app/src/test/java/com/bignerdranch/android/beatbox/ExampleUnitTest.java
new file mode 100644
index 0000000..a8bc989
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/app/src/test/java/com/bignerdranch/android/beatbox/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.beatbox;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/23_XMLDrawables/BeatBox/app/src/test/java/com/bignerdranch/android/beatbox/SoundViewModelTest.java b/Book Files/23_XMLDrawables/BeatBox/app/src/test/java/com/bignerdranch/android/beatbox/SoundViewModelTest.java
new file mode 100644
index 0000000..e38ca2a
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/app/src/test/java/com/bignerdranch/android/beatbox/SoundViewModelTest.java
@@ -0,0 +1,34 @@
+package com.bignerdranch.android.beatbox;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class SoundViewModelTest {
+ private BeatBox mBeatBox;
+ private Sound mSound;
+ private SoundViewModel mSubject;
+
+ @Before
+ public void setUp() throws Exception {
+ mBeatBox = mock(BeatBox.class);
+ mSound = new Sound("assetPath");
+ mSubject = new SoundViewModel(mBeatBox);
+ mSubject.setSound(mSound);
+ }
+
+ @Test
+ public void exposesSoundNameAsTitle() {
+ assertThat(mSubject.getTitle(), is(mSound.getName()));
+ }
+
+ @Test
+ public void callsBeatBoxPlayOnButtonClicked() {
+ mSubject.onButtonClicked();
+ verify(mBeatBox).play(mSound);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/23_XMLDrawables/BeatBox/build.gradle b/Book Files/23_XMLDrawables/BeatBox/build.gradle
new file mode 100644
index 0000000..1ea4bd0
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Book Files/23_XMLDrawables/BeatBox/gradle.properties b/Book Files/23_XMLDrawables/BeatBox/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/Book Files/23_XMLDrawables/BeatBox/gradle/wrapper/gradle-wrapper.jar b/Book Files/23_XMLDrawables/BeatBox/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/Book Files/23_XMLDrawables/BeatBox/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/23_XMLDrawables/BeatBox/gradle/wrapper/gradle-wrapper.properties b/Book Files/23_XMLDrawables/BeatBox/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a82392d
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/23_XMLDrawables/BeatBox/gradlew b/Book Files/23_XMLDrawables/BeatBox/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/23_XMLDrawables/BeatBox/gradlew.bat b/Book Files/23_XMLDrawables/BeatBox/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/23_XMLDrawables/BeatBox/settings.gradle b/Book Files/23_XMLDrawables/BeatBox/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/23_XMLDrawables/BeatBox/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/23_XMLDrawables/LICENSE.txt b/Book Files/23_XMLDrawables/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Book Files/23_XMLDrawables/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Book Files/23_XMLDrawables/README.md b/Book Files/23_XMLDrawables/README.md
new file mode 100644
index 0000000..00d5165
--- /dev/null
+++ b/Book Files/23_XMLDrawables/README.md
@@ -0,0 +1,3 @@
+# Credit goes where credit is due
+
+Bill, Chris, and Brian were responsible for this code. The sounds, though? The sounds in BeatBox/app/src/main/assets/rasslin? We got them from plagasul, at [http://www.freesound.org/people/plagasul/packs/3/](http://www.freesound.org/people/plagasul/packs/3/).
diff --git a/Book Files/24_NerdLauncher/.DS_Store b/Book Files/24_NerdLauncher/.DS_Store
new file mode 100644
index 0000000..5008ddf
Binary files /dev/null and b/Book Files/24_NerdLauncher/.DS_Store differ
diff --git a/Book Files/24_NerdLauncher/LICENSE.txt b/Book Files/24_NerdLauncher/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Book Files/24_NerdLauncher/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/.gitignore b/Book Files/24_NerdLauncher/NerdLauncher/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/Book Files/24_NerdLauncher/NerdLauncher/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/.idea/compiler.xml b/Book Files/24_NerdLauncher/NerdLauncher/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/Book Files/24_NerdLauncher/NerdLauncher/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/.idea/copyright/profiles_settings.xml b/Book Files/24_NerdLauncher/NerdLauncher/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/Book Files/24_NerdLauncher/NerdLauncher/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/.idea/encodings.xml b/Book Files/24_NerdLauncher/NerdLauncher/.idea/encodings.xml
new file mode 100644
index 0000000..97626ba
--- /dev/null
+++ b/Book Files/24_NerdLauncher/NerdLauncher/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/.idea/gradle.xml b/Book Files/24_NerdLauncher/NerdLauncher/.idea/gradle.xml
new file mode 100644
index 0000000..7ac24c7
--- /dev/null
+++ b/Book Files/24_NerdLauncher/NerdLauncher/.idea/gradle.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/.idea/misc.xml b/Book Files/24_NerdLauncher/NerdLauncher/.idea/misc.xml
new file mode 100644
index 0000000..5d19981
--- /dev/null
+++ b/Book Files/24_NerdLauncher/NerdLauncher/.idea/misc.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/.idea/modules.xml b/Book Files/24_NerdLauncher/NerdLauncher/.idea/modules.xml
new file mode 100644
index 0000000..793ccaa
--- /dev/null
+++ b/Book Files/24_NerdLauncher/NerdLauncher/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/.idea/runConfigurations.xml b/Book Files/24_NerdLauncher/NerdLauncher/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/Book Files/24_NerdLauncher/NerdLauncher/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/app/.gitignore b/Book Files/24_NerdLauncher/NerdLauncher/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/24_NerdLauncher/NerdLauncher/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/app/build.gradle b/Book Files/24_NerdLauncher/NerdLauncher/app/build.gradle
new file mode 100644
index 0000000..9e3cb01
--- /dev/null
+++ b/Book Files/24_NerdLauncher/NerdLauncher/app/build.gradle
@@ -0,0 +1,31 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ applicationId "com.bignerdranch.android.nerdlauncher"
+ minSdkVersion 19
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ compile 'com.android.support.constraint:constraint-layout:1.0.2'
+ testCompile 'junit:junit:4.12'
+ compile 'com.android.support:recyclerview-v7:25.3.0'
+}
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/app/proguard-rules.pro b/Book Files/24_NerdLauncher/NerdLauncher/app/proguard-rules.pro
new file mode 100644
index 0000000..82464fa
--- /dev/null
+++ b/Book Files/24_NerdLauncher/NerdLauncher/app/proguard-rules.pro
@@ -0,0 +1,25 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/chris/AndroidDeveloper/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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/Book Files/24_NerdLauncher/NerdLauncher/app/src/androidTest/java/com/bignerdranch/android/nerdlauncher/ExampleInstrumentedTest.java b/Book Files/24_NerdLauncher/NerdLauncher/app/src/androidTest/java/com/bignerdranch/android/nerdlauncher/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..5339e92
--- /dev/null
+++ b/Book Files/24_NerdLauncher/NerdLauncher/app/src/androidTest/java/com/bignerdranch/android/nerdlauncher/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.nerdlauncher;
+
+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.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.bignerdranch.android.nerdlauncher", appContext.getPackageName());
+ }
+}
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/AndroidManifest.xml b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..5563ac2
--- /dev/null
+++ b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/java/com/bignerdranch/android/nerdlauncher/NerdLauncherActivity.java b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/java/com/bignerdranch/android/nerdlauncher/NerdLauncherActivity.java
new file mode 100644
index 0000000..77337a7
--- /dev/null
+++ b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/java/com/bignerdranch/android/nerdlauncher/NerdLauncherActivity.java
@@ -0,0 +1,11 @@
+package com.bignerdranch.android.nerdlauncher;
+
+import android.support.v4.app.Fragment;
+
+public class NerdLauncherActivity extends SingleFragmentActivity {
+
+ @Override
+ protected Fragment createFragment() {
+ return NerdLauncherFragment.newInstance();
+ }
+}
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/java/com/bignerdranch/android/nerdlauncher/NerdLauncherFragment.java b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/java/com/bignerdranch/android/nerdlauncher/NerdLauncherFragment.java
new file mode 100644
index 0000000..884654c
--- /dev/null
+++ b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/java/com/bignerdranch/android/nerdlauncher/NerdLauncherFragment.java
@@ -0,0 +1,119 @@
+package com.bignerdranch.android.nerdlauncher;
+
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+public class NerdLauncherFragment extends Fragment {
+ public static final String TAG = "NerdLauncherFragment";
+
+ private RecyclerView mRecyclerView;
+
+ public static NerdLauncherFragment newInstance() {
+ return new NerdLauncherFragment();
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.fragment_nerd_launcher, container, false);
+ mRecyclerView = (RecyclerView) v.findViewById(R.id.app_recycler_view);
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
+
+ setupAdapter();
+ return v;
+ }
+
+ private void setupAdapter() {
+ Intent startupIntent = new Intent(Intent.ACTION_MAIN);
+ startupIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+
+ PackageManager pm = getActivity().getPackageManager();
+ List activities = pm.queryIntentActivities(startupIntent, 0);
+ Collections.sort(activities, new Comparator() {
+ @Override
+ public int compare(ResolveInfo a, ResolveInfo b) {
+ PackageManager pm = getActivity().getPackageManager();
+ return String.CASE_INSENSITIVE_ORDER.compare(
+ a.loadLabel(pm).toString(),
+ b.loadLabel(pm).toString());
+ }
+ });
+ Log.i(TAG, "Found " + activities.size() + " activities.");
+ mRecyclerView.setAdapter(new ActivityAdapter(activities));
+ }
+
+ private class ActivityHolder extends RecyclerView.ViewHolder
+ implements View.OnClickListener {
+ private ResolveInfo mResolveInfo;
+ private TextView mNameTextView;
+
+ public ActivityHolder(View itemView) {
+ super(itemView);
+ mNameTextView = (TextView) itemView;
+ mNameTextView.setOnClickListener(this);
+ }
+
+ public void bindActivity(ResolveInfo resolveInfo) {
+ mResolveInfo = resolveInfo;
+ PackageManager pm = getActivity().getPackageManager();
+ String appName = mResolveInfo.loadLabel(pm).toString();
+ mNameTextView.setText(appName);
+ }
+
+ @Override
+ public void onClick(View view) {
+ ActivityInfo activityInfo = mResolveInfo.activityInfo;
+
+ Intent i = new Intent(Intent.ACTION_MAIN)
+ .setClassName(activityInfo.applicationInfo.packageName,
+ activityInfo.name)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ startActivity(i);
+ }
+ }
+
+ private class ActivityAdapter extends RecyclerView.Adapter {
+ private final List mActivities;
+
+ public ActivityAdapter(List activities) {
+ mActivities = activities;
+ }
+
+ @Override
+ public ActivityHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
+ View view = layoutInflater
+ .inflate(android.R.layout.simple_list_item_1, parent, false);
+ return new ActivityHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(ActivityHolder holder, int position) {
+ ResolveInfo resolveInfo = mActivities.get(position);
+ holder.bindActivity(resolveInfo);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mActivities.size();
+ }
+ }
+
+
+}
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/java/com/bignerdranch/android/nerdlauncher/SingleFragmentActivity.java b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/java/com/bignerdranch/android/nerdlauncher/SingleFragmentActivity.java
new file mode 100644
index 0000000..2caae82
--- /dev/null
+++ b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/java/com/bignerdranch/android/nerdlauncher/SingleFragmentActivity.java
@@ -0,0 +1,27 @@
+package com.bignerdranch.android.nerdlauncher;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.AppCompatActivity;
+
+public abstract class SingleFragmentActivity extends AppCompatActivity {
+
+ protected abstract Fragment createFragment();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_fragment);
+
+ FragmentManager fm = getSupportFragmentManager();
+ Fragment fragment = fm.findFragmentById(R.id.fragment_container);
+
+ if (fragment == null) {
+ fragment = createFragment();
+ fm.beginTransaction()
+ .add(R.id.fragment_container, fragment)
+ .commit();
+ }
+ }
+}
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/layout/activity_fragment.xml b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/layout/activity_fragment.xml
new file mode 100644
index 0000000..1773143
--- /dev/null
+++ b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/layout/activity_fragment.xml
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/layout/fragment_nerd_launcher.xml b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/layout/fragment_nerd_launcher.xml
new file mode 100644
index 0000000..336e50e
--- /dev/null
+++ b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/layout/fragment_nerd_launcher.xml
@@ -0,0 +1,6 @@
+
+
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000..4c2d454
Binary files /dev/null and b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..56bfbe5
Binary files /dev/null and b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000..52d8318
Binary files /dev/null and b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..d19e914
Binary files /dev/null and b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000..3d70a95
Binary files /dev/null and b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..77c5d2f
Binary files /dev/null and b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..01dad87
Binary files /dev/null and b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..4aa0880
Binary files /dev/null and b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..4d7405f
Binary files /dev/null and b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..a0aebbc
Binary files /dev/null and b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/values/colors.xml b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/values/strings.xml b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..6181fdc
--- /dev/null
+++ b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ NerdLauncher
+
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/values/styles.xml b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/Book Files/24_NerdLauncher/NerdLauncher/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/app/src/test/java/com/bignerdranch/android/nerdlauncher/ExampleUnitTest.java b/Book Files/24_NerdLauncher/NerdLauncher/app/src/test/java/com/bignerdranch/android/nerdlauncher/ExampleUnitTest.java
new file mode 100644
index 0000000..6b53a0b
--- /dev/null
+++ b/Book Files/24_NerdLauncher/NerdLauncher/app/src/test/java/com/bignerdranch/android/nerdlauncher/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.nerdlauncher;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/build.gradle b/Book Files/24_NerdLauncher/NerdLauncher/build.gradle
new file mode 100644
index 0000000..1ea4bd0
--- /dev/null
+++ b/Book Files/24_NerdLauncher/NerdLauncher/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/gradle.properties b/Book Files/24_NerdLauncher/NerdLauncher/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/Book Files/24_NerdLauncher/NerdLauncher/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/gradle/wrapper/gradle-wrapper.jar b/Book Files/24_NerdLauncher/NerdLauncher/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/Book Files/24_NerdLauncher/NerdLauncher/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/gradle/wrapper/gradle-wrapper.properties b/Book Files/24_NerdLauncher/NerdLauncher/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a82392d
--- /dev/null
+++ b/Book Files/24_NerdLauncher/NerdLauncher/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/gradlew b/Book Files/24_NerdLauncher/NerdLauncher/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/Book Files/24_NerdLauncher/NerdLauncher/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/gradlew.bat b/Book Files/24_NerdLauncher/NerdLauncher/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/24_NerdLauncher/NerdLauncher/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/24_NerdLauncher/NerdLauncher/settings.gradle b/Book Files/24_NerdLauncher/NerdLauncher/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/24_NerdLauncher/NerdLauncher/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/25_HTTPBackgroundTasks/LICENSE.txt b/Book Files/25_HTTPBackgroundTasks/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/.gitignore b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/.idea/compiler.xml b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/.idea/copyright/profiles_settings.xml b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/.idea/gradle.xml b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/.idea/gradle.xml
new file mode 100644
index 0000000..7ac24c7
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/.idea/gradle.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/.idea/misc.xml b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/.idea/misc.xml
new file mode 100644
index 0000000..b0a270f
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/.idea/misc.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/.idea/modules.xml b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/.idea/modules.xml
new file mode 100644
index 0000000..9a0b686
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/.idea/runConfigurations.xml b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/.gitignore b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/build.gradle b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/build.gradle
new file mode 100644
index 0000000..3f1ad7b
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/build.gradle
@@ -0,0 +1,30 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ applicationId "com.bignerdranch.android.photogallery"
+ minSdkVersion 19
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ testCompile 'junit:junit:4.12'
+ compile 'com.android.support:recyclerview-v7:25.3.0'
+}
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/proguard-rules.pro b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/proguard-rules.pro
new file mode 100644
index 0000000..cf5610c
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/bphillips/devtools/android-sdk-macosx/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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 *;
+#}
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/androidTest/java/com/bignerdranch/android/photogallery/ExampleInstrumentedTest.java b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/androidTest/java/com/bignerdranch/android/photogallery/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..409b32c
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/androidTest/java/com/bignerdranch/android/photogallery/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.photogallery;
+
+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.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.bignerdranch.android.photogallery", appContext.getPackageName());
+ }
+}
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/AndroidManifest.xml b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..4238714
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/AndroidManifest.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/FlickrFetchr.java b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/FlickrFetchr.java
new file mode 100644
index 0000000..ce27109
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/FlickrFetchr.java
@@ -0,0 +1,98 @@
+package com.bignerdranch.android.photogallery;
+
+import android.net.Uri;
+import android.util.Log;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+public class FlickrFetchr {
+
+ private static final String TAG = "FlickrFetchr";
+
+ private static final String API_KEY = "REPLACE_ME_WITH_A_REAL_KEY";
+
+ public byte[] getUrlBytes(String urlSpec) throws IOException {
+ URL url = new URL(urlSpec);
+ HttpURLConnection connection = (HttpURLConnection)url.openConnection();
+ try {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ InputStream in = connection.getInputStream();
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ throw new IOException(connection.getResponseMessage() +
+ ": with " +
+ urlSpec);
+ }
+ int bytesRead = 0;
+ byte[] buffer = new byte[1024];
+ while ((bytesRead = in.read(buffer)) > 0) {
+ out.write(buffer, 0, bytesRead);
+ }
+ out.close();
+ return out.toByteArray();
+ } finally {
+ connection.disconnect();
+ }
+ }
+ public String getUrlString(String urlSpec) throws IOException {
+ return new String(getUrlBytes(urlSpec));
+ }
+
+ public List fetchItems() {
+
+ List items = new ArrayList<>();
+
+ try {
+ String url = Uri.parse("https://api.flickr.com/services/rest/")
+ .buildUpon()
+ .appendQueryParameter("method", "flickr.photos.getRecent")
+ .appendQueryParameter("api_key", API_KEY)
+ .appendQueryParameter("format", "json")
+ .appendQueryParameter("nojsoncallback", "1")
+ .appendQueryParameter("extras", "url_s")
+ .build().toString();
+ String jsonString = getUrlString(url);
+ Log.i(TAG, "Received JSON: " + jsonString);
+ JSONObject jsonBody = new JSONObject(jsonString);
+ parseItems(items, jsonBody);
+ } catch (IOException ioe) {
+ Log.e(TAG, "Failed to fetch items", ioe);
+ } catch (JSONException je) {
+ Log.e(TAG, "Failed to parse JSON", je);
+ }
+
+ return items;
+ }
+
+ private void parseItems(List items, JSONObject jsonBody)
+ throws IOException, JSONException {
+
+ JSONObject photosJsonObject = jsonBody.getJSONObject("photos");
+ JSONArray photoJsonArray = photosJsonObject.getJSONArray("photo");
+
+ for (int i = 0; i < photoJsonArray.length(); i++) {
+ JSONObject photoJsonObject = photoJsonArray.getJSONObject(i);
+
+ GalleryItem item = new GalleryItem();
+ item.setId(photoJsonObject.getString("id"));
+ item.setCaption(photoJsonObject.getString("title"));
+
+ if (!photoJsonObject.has("url_s")) {
+ continue;
+ }
+
+ item.setUrl(photoJsonObject.getString("url_s"));
+ items.add(item);
+ }
+ }
+
+}
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/GalleryItem.java b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/GalleryItem.java
new file mode 100644
index 0000000..a5a8d4b
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/GalleryItem.java
@@ -0,0 +1,36 @@
+package com.bignerdranch.android.photogallery;
+
+public class GalleryItem {
+ private String mCaption;
+ private String mId;
+ private String mUrl;
+
+ public String getCaption() {
+ return mCaption;
+ }
+
+ public void setCaption(String caption) {
+ mCaption = caption;
+ }
+
+ public String getId() {
+ return mId;
+ }
+
+ public void setId(String id) {
+ mId = id;
+ }
+
+ public String getUrl() {
+ return mUrl;
+ }
+
+ public void setUrl(String url) {
+ mUrl = url;
+ }
+
+ @Override
+ public String toString() {
+ return mCaption;
+ }
+}
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryActivity.java b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryActivity.java
new file mode 100644
index 0000000..0a45dfa
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryActivity.java
@@ -0,0 +1,11 @@
+package com.bignerdranch.android.photogallery;
+
+import android.support.v4.app.Fragment;
+
+public class PhotoGalleryActivity extends SingleFragmentActivity {
+
+ @Override
+ protected Fragment createFragment() {
+ return PhotoGalleryFragment.newInstance();
+ }
+}
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryFragment.java b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryFragment.java
new file mode 100644
index 0000000..37280e2
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryFragment.java
@@ -0,0 +1,106 @@
+package com.bignerdranch.android.photogallery;
+
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class PhotoGalleryFragment extends Fragment {
+
+ private static final String TAG = "PhotoGalleryFragment";
+
+ private RecyclerView mPhotoRecyclerView;
+ private List mItems = new ArrayList<>();
+
+ public static PhotoGalleryFragment newInstance() {
+ return new PhotoGalleryFragment();
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setRetainInstance(true);
+ new FetchItemsTask().execute();
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.fragment_photo_gallery, container, false);
+
+ mPhotoRecyclerView = (RecyclerView) v.findViewById(R.id.photo_recycler_view);
+ mPhotoRecyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 3));
+
+ setupAdapter();
+
+ return v;
+ }
+
+ private void setupAdapter() {
+ if (isAdded()) {
+ mPhotoRecyclerView.setAdapter(new PhotoAdapter(mItems));
+ }
+ }
+
+ private class PhotoHolder extends RecyclerView.ViewHolder {
+ private TextView mTitleTextView;
+
+ public PhotoHolder(View itemView) {
+ super(itemView);
+
+ mTitleTextView = (TextView) itemView;
+ }
+
+ public void bindGalleryItem(GalleryItem item) {
+ mTitleTextView.setText(item.toString());
+ }
+ }
+
+ private class PhotoAdapter extends RecyclerView.Adapter {
+
+ private List mGalleryItems;
+
+ public PhotoAdapter(List galleryItems) {
+ mGalleryItems = galleryItems;
+ }
+
+ @Override
+ public PhotoHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
+ TextView textView = new TextView(getActivity());
+ return new PhotoHolder(textView);
+ }
+
+ @Override
+ public void onBindViewHolder(PhotoHolder photoHolder, int position) {
+ GalleryItem galleryItem = mGalleryItems.get(position);
+ photoHolder.bindGalleryItem(galleryItem);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mGalleryItems.size();
+ }
+ }
+
+ private class FetchItemsTask extends AsyncTask> {
+ @Override
+ protected List doInBackground(Void... params) {
+ return new FlickrFetchr().fetchItems();
+ }
+
+ @Override
+ protected void onPostExecute(List items) {
+ mItems = items;
+ setupAdapter();
+ }
+ }
+}
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/SingleFragmentActivity.java b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/SingleFragmentActivity.java
new file mode 100644
index 0000000..faa98af
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/SingleFragmentActivity.java
@@ -0,0 +1,33 @@
+package com.bignerdranch.android.photogallery;
+
+import android.os.Bundle;
+import android.support.annotation.LayoutRes;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.AppCompatActivity;
+
+public abstract class SingleFragmentActivity extends AppCompatActivity {
+
+ protected abstract Fragment createFragment();
+
+ @LayoutRes
+ protected int getLayoutResId() {
+ return R.layout.activity_fragment;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(getLayoutResId());
+
+ FragmentManager fm = getSupportFragmentManager();
+ Fragment fragment = fm.findFragmentById(R.id.fragment_container);
+
+ if (fragment == null) {
+ fragment = createFragment();
+ fm.beginTransaction()
+ .add(R.id.fragment_container, fragment)
+ .commit();
+ }
+ }
+}
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/layout/activity_fragment.xml b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/layout/activity_fragment.xml
new file mode 100644
index 0000000..1773143
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/layout/activity_fragment.xml
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/layout/fragment_photo_gallery.xml b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/layout/fragment_photo_gallery.xml
new file mode 100644
index 0000000..4af4094
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/layout/fragment_photo_gallery.xml
@@ -0,0 +1,8 @@
+
+
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/values-w820dp/dimens.xml b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/values/colors.xml b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/values/dimens.xml b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/values/strings.xml b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..70d10a4
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ PhotoGallery
+
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/values/styles.xml b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/test/java/com/bignerdranch/android/photogallery/ExampleUnitTest.java b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/test/java/com/bignerdranch/android/photogallery/ExampleUnitTest.java
new file mode 100644
index 0000000..95126a2
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/app/src/test/java/com/bignerdranch/android/photogallery/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.photogallery;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/build.gradle b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/build.gradle
new file mode 100644
index 0000000..1ea4bd0
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/gradle.properties b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/gradle/wrapper/gradle-wrapper.jar b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/gradle/wrapper/gradle-wrapper.properties b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a82392d
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/gradlew b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/gradlew.bat b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/25_HTTPBackgroundTasks/PhotoGallery/settings.gradle b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/25_HTTPBackgroundTasks/PhotoGallery/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/26_Handlers/LICENSE.txt b/Book Files/26_Handlers/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Book Files/26_Handlers/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Book Files/26_Handlers/PhotoGallery/.gitignore b/Book Files/26_Handlers/PhotoGallery/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Book Files/26_Handlers/PhotoGallery/.idea/compiler.xml b/Book Files/26_Handlers/PhotoGallery/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/26_Handlers/PhotoGallery/.idea/copyright/profiles_settings.xml b/Book Files/26_Handlers/PhotoGallery/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Book Files/26_Handlers/PhotoGallery/.idea/gradle.xml b/Book Files/26_Handlers/PhotoGallery/.idea/gradle.xml
new file mode 100644
index 0000000..7ac24c7
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/.idea/gradle.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/26_Handlers/PhotoGallery/.idea/misc.xml b/Book Files/26_Handlers/PhotoGallery/.idea/misc.xml
new file mode 100644
index 0000000..b0a270f
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/.idea/misc.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/26_Handlers/PhotoGallery/.idea/modules.xml b/Book Files/26_Handlers/PhotoGallery/.idea/modules.xml
new file mode 100644
index 0000000..9a0b686
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/26_Handlers/PhotoGallery/.idea/runConfigurations.xml b/Book Files/26_Handlers/PhotoGallery/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/26_Handlers/PhotoGallery/app/.gitignore b/Book Files/26_Handlers/PhotoGallery/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/26_Handlers/PhotoGallery/app/build.gradle b/Book Files/26_Handlers/PhotoGallery/app/build.gradle
new file mode 100644
index 0000000..3f1ad7b
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/app/build.gradle
@@ -0,0 +1,30 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ applicationId "com.bignerdranch.android.photogallery"
+ minSdkVersion 19
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ testCompile 'junit:junit:4.12'
+ compile 'com.android.support:recyclerview-v7:25.3.0'
+}
diff --git a/Book Files/26_Handlers/PhotoGallery/app/proguard-rules.pro b/Book Files/26_Handlers/PhotoGallery/app/proguard-rules.pro
new file mode 100644
index 0000000..cf5610c
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/bphillips/devtools/android-sdk-macosx/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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 *;
+#}
diff --git a/Book Files/26_Handlers/PhotoGallery/app/src/androidTest/java/com/bignerdranch/android/photogallery/ExampleInstrumentedTest.java b/Book Files/26_Handlers/PhotoGallery/app/src/androidTest/java/com/bignerdranch/android/photogallery/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..409b32c
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/app/src/androidTest/java/com/bignerdranch/android/photogallery/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.photogallery;
+
+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.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.bignerdranch.android.photogallery", appContext.getPackageName());
+ }
+}
diff --git a/Book Files/26_Handlers/PhotoGallery/app/src/main/AndroidManifest.xml b/Book Files/26_Handlers/PhotoGallery/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..4238714
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/app/src/main/AndroidManifest.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Book Files/26_Handlers/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/FlickrFetchr.java b/Book Files/26_Handlers/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/FlickrFetchr.java
new file mode 100644
index 0000000..ce27109
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/FlickrFetchr.java
@@ -0,0 +1,98 @@
+package com.bignerdranch.android.photogallery;
+
+import android.net.Uri;
+import android.util.Log;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+public class FlickrFetchr {
+
+ private static final String TAG = "FlickrFetchr";
+
+ private static final String API_KEY = "REPLACE_ME_WITH_A_REAL_KEY";
+
+ public byte[] getUrlBytes(String urlSpec) throws IOException {
+ URL url = new URL(urlSpec);
+ HttpURLConnection connection = (HttpURLConnection)url.openConnection();
+ try {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ InputStream in = connection.getInputStream();
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ throw new IOException(connection.getResponseMessage() +
+ ": with " +
+ urlSpec);
+ }
+ int bytesRead = 0;
+ byte[] buffer = new byte[1024];
+ while ((bytesRead = in.read(buffer)) > 0) {
+ out.write(buffer, 0, bytesRead);
+ }
+ out.close();
+ return out.toByteArray();
+ } finally {
+ connection.disconnect();
+ }
+ }
+ public String getUrlString(String urlSpec) throws IOException {
+ return new String(getUrlBytes(urlSpec));
+ }
+
+ public List fetchItems() {
+
+ List items = new ArrayList<>();
+
+ try {
+ String url = Uri.parse("https://api.flickr.com/services/rest/")
+ .buildUpon()
+ .appendQueryParameter("method", "flickr.photos.getRecent")
+ .appendQueryParameter("api_key", API_KEY)
+ .appendQueryParameter("format", "json")
+ .appendQueryParameter("nojsoncallback", "1")
+ .appendQueryParameter("extras", "url_s")
+ .build().toString();
+ String jsonString = getUrlString(url);
+ Log.i(TAG, "Received JSON: " + jsonString);
+ JSONObject jsonBody = new JSONObject(jsonString);
+ parseItems(items, jsonBody);
+ } catch (IOException ioe) {
+ Log.e(TAG, "Failed to fetch items", ioe);
+ } catch (JSONException je) {
+ Log.e(TAG, "Failed to parse JSON", je);
+ }
+
+ return items;
+ }
+
+ private void parseItems(List items, JSONObject jsonBody)
+ throws IOException, JSONException {
+
+ JSONObject photosJsonObject = jsonBody.getJSONObject("photos");
+ JSONArray photoJsonArray = photosJsonObject.getJSONArray("photo");
+
+ for (int i = 0; i < photoJsonArray.length(); i++) {
+ JSONObject photoJsonObject = photoJsonArray.getJSONObject(i);
+
+ GalleryItem item = new GalleryItem();
+ item.setId(photoJsonObject.getString("id"));
+ item.setCaption(photoJsonObject.getString("title"));
+
+ if (!photoJsonObject.has("url_s")) {
+ continue;
+ }
+
+ item.setUrl(photoJsonObject.getString("url_s"));
+ items.add(item);
+ }
+ }
+
+}
diff --git a/Book Files/26_Handlers/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/GalleryItem.java b/Book Files/26_Handlers/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/GalleryItem.java
new file mode 100644
index 0000000..41af929
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/GalleryItem.java
@@ -0,0 +1,35 @@
+package com.bignerdranch.android.photogallery;
+
+public class GalleryItem {
+ private String mCaption;
+ private String mId;
+ private String mUrl;
+
+ public String getCaption() {
+ return mCaption;
+ }
+
+ public void setCaption(String caption) {
+ mCaption = caption;
+ }
+
+ public String getId() {
+ return mId;
+ }
+
+ public void setId(String id) {
+ mId = id;
+ }
+
+ public String getUrl() {
+ return mUrl;
+ }
+
+ public void setUrl(String url) {
+ mUrl = url;
+ }
+ @Override
+ public String toString() {
+ return mCaption;
+ }
+}
diff --git a/Book Files/26_Handlers/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryActivity.java b/Book Files/26_Handlers/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryActivity.java
new file mode 100644
index 0000000..0a45dfa
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryActivity.java
@@ -0,0 +1,11 @@
+package com.bignerdranch.android.photogallery;
+
+import android.support.v4.app.Fragment;
+
+public class PhotoGalleryActivity extends SingleFragmentActivity {
+
+ @Override
+ protected Fragment createFragment() {
+ return PhotoGalleryFragment.newInstance();
+ }
+}
diff --git a/Book Files/26_Handlers/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryFragment.java b/Book Files/26_Handlers/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryFragment.java
new file mode 100644
index 0000000..18799ad
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryFragment.java
@@ -0,0 +1,144 @@
+package com.bignerdranch.android.photogallery;
+
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PhotoGalleryFragment extends Fragment {
+ private static final String TAG = "PhotoGalleryFragment";
+
+ private RecyclerView mPhotoRecyclerView;
+ private List mItems = new ArrayList<>();
+ private ThumbnailDownloader mThumbnailDownloader;
+
+ public static PhotoGalleryFragment newInstance() {
+ return new PhotoGalleryFragment();
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setRetainInstance(true);
+ new FetchItemsTask().execute();
+
+ Handler responseHandler = new Handler();
+ mThumbnailDownloader = new ThumbnailDownloader<>(responseHandler);
+ mThumbnailDownloader.setThumbnailDownloadListener(
+ new ThumbnailDownloader.ThumbnailDownloadListener() {
+ @Override
+ public void onThumbnailDownloaded(PhotoHolder photoHolder, Bitmap bitmap) {
+ Drawable drawable = new BitmapDrawable(getResources(), bitmap);
+ photoHolder.bindDrawable(drawable);
+ }
+ }
+ );
+ mThumbnailDownloader.start();
+ mThumbnailDownloader.getLooper();
+ Log.i(TAG, "Background thread started");
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.fragment_photo_gallery, container, false);
+
+ mPhotoRecyclerView = (RecyclerView) v.findViewById(R.id.photo_recycler_view);
+ mPhotoRecyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 3));
+
+ setupAdapter();
+
+ return v;
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ mThumbnailDownloader.clearQueue();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mThumbnailDownloader.quit();
+ Log.i(TAG, "Background thread destroyed");
+ }
+
+ private void setupAdapter() {
+ if (isAdded()) {
+ mPhotoRecyclerView.setAdapter(new PhotoAdapter(mItems));
+ }
+ }
+
+ private class PhotoHolder extends RecyclerView.ViewHolder {
+ private ImageView mItemImageView;
+
+ public PhotoHolder(View itemView) {
+ super(itemView);
+
+ mItemImageView = (ImageView) itemView.findViewById(R.id.item_image_view);
+ }
+
+ public void bindDrawable(Drawable drawable) {
+ mItemImageView.setImageDrawable(drawable);
+ }
+ }
+
+ private class PhotoAdapter extends RecyclerView.Adapter {
+
+ private List mGalleryItems;
+
+ public PhotoAdapter(List galleryItems) {
+ mGalleryItems = galleryItems;
+ }
+
+ @Override
+ public PhotoHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
+ LayoutInflater inflater = LayoutInflater.from(getActivity());
+ View view = inflater.inflate(R.layout.list_item_gallery, viewGroup, false);
+ return new PhotoHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(PhotoHolder photoHolder, int position) {
+ GalleryItem galleryItem = mGalleryItems.get(position);
+ Drawable placeholder = getResources().getDrawable(R.drawable.bill_up_close);
+ photoHolder.bindDrawable(placeholder);
+ mThumbnailDownloader.queueThumbnail(photoHolder, galleryItem.getUrl());
+ }
+
+ @Override
+ public int getItemCount() {
+ return mGalleryItems.size();
+ }
+ }
+
+ private class FetchItemsTask extends AsyncTask> {
+
+ @Override
+ protected List doInBackground(Void... params) {
+ return new FlickrFetchr().fetchItems();
+ }
+
+ @Override
+ protected void onPostExecute(List items) {
+ mItems = items;
+ setupAdapter();
+ }
+
+ }
+
+}
diff --git a/Book Files/26_Handlers/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/SingleFragmentActivity.java b/Book Files/26_Handlers/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/SingleFragmentActivity.java
new file mode 100644
index 0000000..faa98af
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/SingleFragmentActivity.java
@@ -0,0 +1,33 @@
+package com.bignerdranch.android.photogallery;
+
+import android.os.Bundle;
+import android.support.annotation.LayoutRes;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.AppCompatActivity;
+
+public abstract class SingleFragmentActivity extends AppCompatActivity {
+
+ protected abstract Fragment createFragment();
+
+ @LayoutRes
+ protected int getLayoutResId() {
+ return R.layout.activity_fragment;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(getLayoutResId());
+
+ FragmentManager fm = getSupportFragmentManager();
+ Fragment fragment = fm.findFragmentById(R.id.fragment_container);
+
+ if (fragment == null) {
+ fragment = createFragment();
+ fm.beginTransaction()
+ .add(R.id.fragment_container, fragment)
+ .commit();
+ }
+ }
+}
diff --git a/Book Files/26_Handlers/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/ThumbnailDownloader.java b/Book Files/26_Handlers/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/ThumbnailDownloader.java
new file mode 100644
index 0000000..d3fc81b
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/ThumbnailDownloader.java
@@ -0,0 +1,102 @@
+package com.bignerdranch.android.photogallery;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+public class ThumbnailDownloader extends HandlerThread {
+ private static final String TAG = "ThumbnailDownloader";
+ private static final int MESSAGE_DOWNLOAD = 0;
+
+ private boolean mHasQuit = false;
+ private Handler mRequestHandler;
+ private ConcurrentMap mRequestMap = new ConcurrentHashMap<>();
+ private Handler mResponseHandler;
+ private ThumbnailDownloadListener mThumbnailDownloadListener;
+
+ public interface ThumbnailDownloadListener {
+ void onThumbnailDownloaded(T target, Bitmap bitmap);
+ }
+
+ public void setThumbnailDownloadListener(ThumbnailDownloadListener listener) {
+ mThumbnailDownloadListener = listener;
+ }
+
+ public ThumbnailDownloader(Handler responseHandler) {
+ super(TAG);
+ mResponseHandler = responseHandler;
+ }
+
+ @Override
+ protected void onLooperPrepared() {
+ mRequestHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MESSAGE_DOWNLOAD) {
+ T target = (T) msg.obj;
+ Log.i(TAG, "Got a request for URL: " + mRequestMap.get(target));
+ handleRequest(target);
+ }
+ }
+ };
+ }
+
+ @Override
+ public boolean quit() {
+ mHasQuit = true;
+ return super.quit();
+ }
+
+ public void queueThumbnail(T target, String url) {
+ Log.i(TAG, "Got a URL: " + url);
+
+ if (url == null) {
+ mRequestMap.remove(target);
+ } else {
+ mRequestMap.put(target, url);
+ mRequestHandler.obtainMessage(MESSAGE_DOWNLOAD, target)
+ .sendToTarget();
+ }
+ }
+
+ public void clearQueue() {
+ mRequestHandler.removeMessages(MESSAGE_DOWNLOAD);
+ mRequestMap.clear();
+ }
+
+ private void handleRequest(final T target) {
+ try {
+ final String url = mRequestMap.get(target);
+
+ if (url == null) {
+ return;
+ }
+
+ byte[] bitmapBytes = new FlickrFetchr().getUrlBytes(url);
+ final Bitmap bitmap = BitmapFactory
+ .decodeByteArray(bitmapBytes, 0, bitmapBytes.length);
+ Log.i(TAG, "Bitmap created");
+
+ mResponseHandler.post(new Runnable() {
+ public void run() {
+ if (mRequestMap.get(target) != url ||
+ mHasQuit) {
+ return;
+ }
+
+ mRequestMap.remove(target);
+ mThumbnailDownloadListener.onThumbnailDownloaded(target, bitmap);
+ }
+ });
+ } catch (IOException ioe) {
+ Log.e(TAG, "Error downloading image", ioe);
+ }
+ }
+}
diff --git a/Book Files/26_Handlers/PhotoGallery/app/src/main/res/drawable/bill_up_close.png b/Book Files/26_Handlers/PhotoGallery/app/src/main/res/drawable/bill_up_close.png
new file mode 100644
index 0000000..1e85792
Binary files /dev/null and b/Book Files/26_Handlers/PhotoGallery/app/src/main/res/drawable/bill_up_close.png differ
diff --git a/Book Files/26_Handlers/PhotoGallery/app/src/main/res/layout/activity_fragment.xml b/Book Files/26_Handlers/PhotoGallery/app/src/main/res/layout/activity_fragment.xml
new file mode 100644
index 0000000..1773143
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/app/src/main/res/layout/activity_fragment.xml
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/Book Files/26_Handlers/PhotoGallery/app/src/main/res/layout/fragment_photo_gallery.xml b/Book Files/26_Handlers/PhotoGallery/app/src/main/res/layout/fragment_photo_gallery.xml
new file mode 100644
index 0000000..4af4094
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/app/src/main/res/layout/fragment_photo_gallery.xml
@@ -0,0 +1,8 @@
+
+
diff --git a/Book Files/26_Handlers/PhotoGallery/app/src/main/res/layout/list_item_gallery.xml b/Book Files/26_Handlers/PhotoGallery/app/src/main/res/layout/list_item_gallery.xml
new file mode 100644
index 0000000..176311a
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/app/src/main/res/layout/list_item_gallery.xml
@@ -0,0 +1,7 @@
+
+
diff --git a/Book Files/26_Handlers/PhotoGallery/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Book Files/26_Handlers/PhotoGallery/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/Book Files/26_Handlers/PhotoGallery/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Book Files/26_Handlers/PhotoGallery/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Book Files/26_Handlers/PhotoGallery/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/Book Files/26_Handlers/PhotoGallery/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Book Files/26_Handlers/PhotoGallery/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Book Files/26_Handlers/PhotoGallery/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/Book Files/26_Handlers/PhotoGallery/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Book Files/26_Handlers/PhotoGallery/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Book Files/26_Handlers/PhotoGallery/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/Book Files/26_Handlers/PhotoGallery/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Book Files/26_Handlers/PhotoGallery/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Book Files/26_Handlers/PhotoGallery/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/Book Files/26_Handlers/PhotoGallery/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Book Files/26_Handlers/PhotoGallery/app/src/main/res/values-w820dp/dimens.xml b/Book Files/26_Handlers/PhotoGallery/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Book Files/26_Handlers/PhotoGallery/app/src/main/res/values/colors.xml b/Book Files/26_Handlers/PhotoGallery/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/Book Files/26_Handlers/PhotoGallery/app/src/main/res/values/dimens.xml b/Book Files/26_Handlers/PhotoGallery/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/Book Files/26_Handlers/PhotoGallery/app/src/main/res/values/strings.xml b/Book Files/26_Handlers/PhotoGallery/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..70d10a4
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ PhotoGallery
+
diff --git a/Book Files/26_Handlers/PhotoGallery/app/src/main/res/values/styles.xml b/Book Files/26_Handlers/PhotoGallery/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Book Files/26_Handlers/PhotoGallery/app/src/test/java/com/bignerdranch/android/photogallery/ExampleUnitTest.java b/Book Files/26_Handlers/PhotoGallery/app/src/test/java/com/bignerdranch/android/photogallery/ExampleUnitTest.java
new file mode 100644
index 0000000..95126a2
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/app/src/test/java/com/bignerdranch/android/photogallery/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.photogallery;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/26_Handlers/PhotoGallery/build.gradle b/Book Files/26_Handlers/PhotoGallery/build.gradle
new file mode 100644
index 0000000..1ea4bd0
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Book Files/26_Handlers/PhotoGallery/gradle.properties b/Book Files/26_Handlers/PhotoGallery/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/Book Files/26_Handlers/PhotoGallery/gradle/wrapper/gradle-wrapper.jar b/Book Files/26_Handlers/PhotoGallery/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/Book Files/26_Handlers/PhotoGallery/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/26_Handlers/PhotoGallery/gradle/wrapper/gradle-wrapper.properties b/Book Files/26_Handlers/PhotoGallery/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a82392d
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/26_Handlers/PhotoGallery/gradlew b/Book Files/26_Handlers/PhotoGallery/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/26_Handlers/PhotoGallery/gradlew.bat b/Book Files/26_Handlers/PhotoGallery/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/26_Handlers/PhotoGallery/settings.gradle b/Book Files/26_Handlers/PhotoGallery/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/26_Handlers/PhotoGallery/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/27_Search/.DS_Store b/Book Files/27_Search/.DS_Store
new file mode 100755
index 0000000..d2f66b1
Binary files /dev/null and b/Book Files/27_Search/.DS_Store differ
diff --git a/Book Files/27_Search/LICENSE.txt b/Book Files/27_Search/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Book Files/27_Search/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Book Files/27_Search/PhotoGallery/.gitignore b/Book Files/27_Search/PhotoGallery/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Book Files/27_Search/PhotoGallery/.idea/compiler.xml b/Book Files/27_Search/PhotoGallery/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/27_Search/PhotoGallery/.idea/copyright/profiles_settings.xml b/Book Files/27_Search/PhotoGallery/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Book Files/27_Search/PhotoGallery/.idea/gradle.xml b/Book Files/27_Search/PhotoGallery/.idea/gradle.xml
new file mode 100644
index 0000000..7ac24c7
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/.idea/gradle.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/27_Search/PhotoGallery/.idea/misc.xml b/Book Files/27_Search/PhotoGallery/.idea/misc.xml
new file mode 100644
index 0000000..b0a270f
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/.idea/misc.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/27_Search/PhotoGallery/.idea/modules.xml b/Book Files/27_Search/PhotoGallery/.idea/modules.xml
new file mode 100644
index 0000000..9a0b686
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/27_Search/PhotoGallery/.idea/runConfigurations.xml b/Book Files/27_Search/PhotoGallery/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/27_Search/PhotoGallery/app/.gitignore b/Book Files/27_Search/PhotoGallery/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/27_Search/PhotoGallery/app/build.gradle b/Book Files/27_Search/PhotoGallery/app/build.gradle
new file mode 100644
index 0000000..3f1ad7b
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/app/build.gradle
@@ -0,0 +1,30 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ applicationId "com.bignerdranch.android.photogallery"
+ minSdkVersion 19
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ testCompile 'junit:junit:4.12'
+ compile 'com.android.support:recyclerview-v7:25.3.0'
+}
diff --git a/Book Files/27_Search/PhotoGallery/app/proguard-rules.pro b/Book Files/27_Search/PhotoGallery/app/proguard-rules.pro
new file mode 100644
index 0000000..cf5610c
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/bphillips/devtools/android-sdk-macosx/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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 *;
+#}
diff --git a/Book Files/27_Search/PhotoGallery/app/src/androidTest/java/com/bignerdranch/android/photogallery/ExampleInstrumentedTest.java b/Book Files/27_Search/PhotoGallery/app/src/androidTest/java/com/bignerdranch/android/photogallery/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..409b32c
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/app/src/androidTest/java/com/bignerdranch/android/photogallery/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.photogallery;
+
+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.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.bignerdranch.android.photogallery", appContext.getPackageName());
+ }
+}
diff --git a/Book Files/27_Search/PhotoGallery/app/src/main/AndroidManifest.xml b/Book Files/27_Search/PhotoGallery/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..4238714
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/app/src/main/AndroidManifest.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Book Files/27_Search/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/FlickrFetchr.java b/Book Files/27_Search/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/FlickrFetchr.java
new file mode 100644
index 0000000..36517c5
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/FlickrFetchr.java
@@ -0,0 +1,120 @@
+package com.bignerdranch.android.photogallery;
+
+import android.net.Uri;
+import android.util.Log;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+public class FlickrFetchr {
+ private static final String TAG = "FlickrFetchr";
+
+ private static final String API_KEY = "REPLACE_ME_WITH_A_REAL_KEY";
+ private static final String FETCH_RECENTS_METHOD = "flickr.photos.getRecent";
+ private static final String SEARCH_METHOD = "flickr.photos.search";
+ private static final Uri ENDPOINT = Uri
+ .parse("https://api.flickr.com/services/rest/")
+ .buildUpon()
+ .appendQueryParameter("api_key", API_KEY)
+ .appendQueryParameter("format", "json")
+ .appendQueryParameter("nojsoncallback", "1")
+ .appendQueryParameter("extras", "url_s")
+ .build();
+
+ public byte[] getUrlBytes(String urlSpec) throws IOException {
+ URL url = new URL(urlSpec);
+ HttpURLConnection connection = (HttpURLConnection)url.openConnection();
+ try {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ InputStream in = connection.getInputStream();
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ throw new IOException(connection.getResponseMessage() +
+ ": with " +
+ urlSpec);
+ }
+ int bytesRead = 0;
+ byte[] buffer = new byte[1024];
+ while ((bytesRead = in.read(buffer)) > 0) {
+ out.write(buffer, 0, bytesRead);
+ }
+ out.close();
+ return out.toByteArray();
+ } finally {
+ connection.disconnect();
+ }
+ }
+
+ public String getUrlString(String urlSpec) throws IOException {
+ return new String(getUrlBytes(urlSpec));
+ }
+
+ public List fetchRecentPhotos() {
+ String url = buildUrl(FETCH_RECENTS_METHOD, null);
+ return downloadGalleryItems(url);
+ }
+
+ public List searchPhotos(String query) {
+ String url = buildUrl(SEARCH_METHOD, query);
+ return downloadGalleryItems(url);
+ }
+
+ private List downloadGalleryItems(String url) {
+ List items = new ArrayList<>();
+
+ try {
+ String jsonString = getUrlString(url);
+ Log.i(TAG, "Received JSON: " + jsonString);
+ JSONObject jsonBody = new JSONObject(jsonString);
+ parseItems(items, jsonBody);
+ } catch (IOException ioe) {
+ Log.e(TAG, "Failed to fetch items", ioe);
+ } catch (JSONException je) {
+ Log.e(TAG, "Failed to parse JSON", je);
+ }
+
+ return items;
+ }
+
+ private String buildUrl(String method, String query) {
+ Uri.Builder uriBuilder = ENDPOINT.buildUpon()
+ .appendQueryParameter("method", method);
+
+ if (method.equals(SEARCH_METHOD)) {
+ uriBuilder.appendQueryParameter("text", query);
+ }
+
+ return uriBuilder.build().toString();
+ }
+
+ private void parseItems(List items, JSONObject jsonBody)
+ throws IOException, JSONException {
+
+ JSONObject photosJsonObject = jsonBody.getJSONObject("photos");
+ JSONArray photoJsonArray = photosJsonObject.getJSONArray("photo");
+
+ for (int i = 0; i < photoJsonArray.length(); i++) {
+ JSONObject photoJsonObject = photoJsonArray.getJSONObject(i);
+
+ GalleryItem item = new GalleryItem();
+ item.setId(photoJsonObject.getString("id"));
+ item.setCaption(photoJsonObject.getString("title"));
+
+ if (!photoJsonObject.has("url_s")) {
+ continue;
+ }
+
+ item.setUrl(photoJsonObject.getString("url_s"));
+ items.add(item);
+ }
+ }
+
+}
diff --git a/Book Files/27_Search/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/GalleryItem.java b/Book Files/27_Search/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/GalleryItem.java
new file mode 100644
index 0000000..a5a8d4b
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/GalleryItem.java
@@ -0,0 +1,36 @@
+package com.bignerdranch.android.photogallery;
+
+public class GalleryItem {
+ private String mCaption;
+ private String mId;
+ private String mUrl;
+
+ public String getCaption() {
+ return mCaption;
+ }
+
+ public void setCaption(String caption) {
+ mCaption = caption;
+ }
+
+ public String getId() {
+ return mId;
+ }
+
+ public void setId(String id) {
+ mId = id;
+ }
+
+ public String getUrl() {
+ return mUrl;
+ }
+
+ public void setUrl(String url) {
+ mUrl = url;
+ }
+
+ @Override
+ public String toString() {
+ return mCaption;
+ }
+}
diff --git a/Book Files/27_Search/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryActivity.java b/Book Files/27_Search/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryActivity.java
new file mode 100644
index 0000000..0a45dfa
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryActivity.java
@@ -0,0 +1,11 @@
+package com.bignerdranch.android.photogallery;
+
+import android.support.v4.app.Fragment;
+
+public class PhotoGalleryActivity extends SingleFragmentActivity {
+
+ @Override
+ protected Fragment createFragment() {
+ return PhotoGalleryFragment.newInstance();
+ }
+}
diff --git a/Book Files/27_Search/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryFragment.java b/Book Files/27_Search/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryFragment.java
new file mode 100644
index 0000000..d605017
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryFragment.java
@@ -0,0 +1,209 @@
+package com.bignerdranch.android.photogallery;
+
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.SearchView;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PhotoGalleryFragment extends Fragment {
+ private static final String TAG = "PhotoGalleryFragment";
+
+ private RecyclerView mPhotoRecyclerView;
+ private List mItems = new ArrayList<>();
+ private ThumbnailDownloader mThumbnailDownloader;
+
+ public static PhotoGalleryFragment newInstance() {
+ return new PhotoGalleryFragment();
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setRetainInstance(true);
+ setHasOptionsMenu(true);
+ updateItems();
+
+ Handler responseHandler = new Handler();
+ mThumbnailDownloader = new ThumbnailDownloader<>(responseHandler);
+ mThumbnailDownloader.setThumbnailDownloadListener(
+ new ThumbnailDownloader.ThumbnailDownloadListener() {
+ @Override
+ public void onThumbnailDownloaded(PhotoHolder photoHolder, Bitmap bitmap) {
+ Drawable drawable = new BitmapDrawable(getResources(), bitmap);
+ photoHolder.bindDrawable(drawable);
+ }
+ }
+ );
+ mThumbnailDownloader.start();
+ mThumbnailDownloader.getLooper();
+ Log.i(TAG, "Background thread started");
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.fragment_photo_gallery, container, false);
+
+ mPhotoRecyclerView = (RecyclerView) v.findViewById(R.id.photo_recycler_view);
+ mPhotoRecyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 3));
+
+ setupAdapter();
+
+ return v;
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ mThumbnailDownloader.clearQueue();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mThumbnailDownloader.quit();
+ Log.i(TAG, "Background thread destroyed");
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
+ super.onCreateOptionsMenu(menu, menuInflater);
+ menuInflater.inflate(R.menu.fragment_photo_gallery, menu);
+ MenuItem searchItem = menu.findItem(R.id.menu_item_search);
+ final SearchView searchView = (SearchView) searchItem.getActionView();
+
+ searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ Log.d(TAG, "QueryTextSubmit: " + s);
+ QueryPreferences.setStoredQuery(getActivity(), s);
+ updateItems();
+ return true;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String s) {
+ Log.d(TAG, "QueryTextChange: " + s);
+ return false;
+ }
+ });
+
+ searchView.setOnSearchClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ String query = QueryPreferences.getStoredQuery(getActivity());
+ searchView.setQuery(query, false);
+ }
+ });
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_item_clear:
+ QueryPreferences.setStoredQuery(getActivity(), null);
+ updateItems();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private void updateItems() {
+ String query = QueryPreferences.getStoredQuery(getActivity());
+ new FetchItemsTask(query).execute();
+ }
+
+ private void setupAdapter() {
+ if (isAdded()) {
+ mPhotoRecyclerView.setAdapter(new PhotoAdapter(mItems));
+ }
+ }
+
+ private class PhotoHolder extends RecyclerView.ViewHolder {
+ private ImageView mItemImageView;
+
+ public PhotoHolder(View itemView) {
+ super(itemView);
+
+ mItemImageView = (ImageView) itemView.findViewById(R.id.item_image_view);
+ }
+
+ public void bindDrawable(Drawable drawable) {
+ mItemImageView.setImageDrawable(drawable);
+ }
+ }
+
+ private class PhotoAdapter extends RecyclerView.Adapter {
+
+ private List mGalleryItems;
+
+ public PhotoAdapter(List galleryItems) {
+ mGalleryItems = galleryItems;
+ }
+
+ @Override
+ public PhotoHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
+ LayoutInflater inflater = LayoutInflater.from(getActivity());
+ View view = inflater.inflate(R.layout.list_item_gallery, viewGroup, false);
+ return new PhotoHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(PhotoHolder photoHolder, int position) {
+ GalleryItem galleryItem = mGalleryItems.get(position);
+ Drawable placeholder = getResources().getDrawable(R.drawable.bill_up_close);
+ photoHolder.bindDrawable(placeholder);
+ mThumbnailDownloader.queueThumbnail(photoHolder, galleryItem.getUrl());
+ }
+
+ @Override
+ public int getItemCount() {
+ return mGalleryItems.size();
+ }
+ }
+
+ private class FetchItemsTask extends AsyncTask> {
+
+ private String mQuery;
+
+ public FetchItemsTask(String query) {
+ mQuery = query;
+ }
+
+ @Override
+ protected List doInBackground(Void... params) {
+
+ if (mQuery == null) {
+ return new FlickrFetchr().fetchRecentPhotos();
+ } else {
+ return new FlickrFetchr().searchPhotos(mQuery);
+ }
+ }
+
+ @Override
+ protected void onPostExecute(List items) {
+ mItems = items;
+ setupAdapter();
+ }
+
+ }
+
+}
diff --git a/Book Files/27_Search/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/QueryPreferences.java b/Book Files/27_Search/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/QueryPreferences.java
new file mode 100644
index 0000000..23e40f4
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/QueryPreferences.java
@@ -0,0 +1,21 @@
+package com.bignerdranch.android.photogallery;
+
+import android.content.Context;
+import android.preference.PreferenceManager;
+
+public class QueryPreferences {
+
+ private static final String PREF_SEARCH_QUERY = "searchQuery";
+
+ public static String getStoredQuery(Context context) {
+ return PreferenceManager.getDefaultSharedPreferences(context)
+ .getString(PREF_SEARCH_QUERY, null);
+ }
+
+ public static void setStoredQuery(Context context, String query) {
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .putString(PREF_SEARCH_QUERY, query)
+ .apply();
+ }
+}
diff --git a/Book Files/27_Search/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/SingleFragmentActivity.java b/Book Files/27_Search/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/SingleFragmentActivity.java
new file mode 100644
index 0000000..faa98af
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/SingleFragmentActivity.java
@@ -0,0 +1,33 @@
+package com.bignerdranch.android.photogallery;
+
+import android.os.Bundle;
+import android.support.annotation.LayoutRes;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.AppCompatActivity;
+
+public abstract class SingleFragmentActivity extends AppCompatActivity {
+
+ protected abstract Fragment createFragment();
+
+ @LayoutRes
+ protected int getLayoutResId() {
+ return R.layout.activity_fragment;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(getLayoutResId());
+
+ FragmentManager fm = getSupportFragmentManager();
+ Fragment fragment = fm.findFragmentById(R.id.fragment_container);
+
+ if (fragment == null) {
+ fragment = createFragment();
+ fm.beginTransaction()
+ .add(R.id.fragment_container, fragment)
+ .commit();
+ }
+ }
+}
diff --git a/Book Files/27_Search/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/ThumbnailDownloader.java b/Book Files/27_Search/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/ThumbnailDownloader.java
new file mode 100644
index 0000000..d3fc81b
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/ThumbnailDownloader.java
@@ -0,0 +1,102 @@
+package com.bignerdranch.android.photogallery;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+public class ThumbnailDownloader extends HandlerThread {
+ private static final String TAG = "ThumbnailDownloader";
+ private static final int MESSAGE_DOWNLOAD = 0;
+
+ private boolean mHasQuit = false;
+ private Handler mRequestHandler;
+ private ConcurrentMap mRequestMap = new ConcurrentHashMap<>();
+ private Handler mResponseHandler;
+ private ThumbnailDownloadListener mThumbnailDownloadListener;
+
+ public interface ThumbnailDownloadListener {
+ void onThumbnailDownloaded(T target, Bitmap bitmap);
+ }
+
+ public void setThumbnailDownloadListener(ThumbnailDownloadListener listener) {
+ mThumbnailDownloadListener = listener;
+ }
+
+ public ThumbnailDownloader(Handler responseHandler) {
+ super(TAG);
+ mResponseHandler = responseHandler;
+ }
+
+ @Override
+ protected void onLooperPrepared() {
+ mRequestHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MESSAGE_DOWNLOAD) {
+ T target = (T) msg.obj;
+ Log.i(TAG, "Got a request for URL: " + mRequestMap.get(target));
+ handleRequest(target);
+ }
+ }
+ };
+ }
+
+ @Override
+ public boolean quit() {
+ mHasQuit = true;
+ return super.quit();
+ }
+
+ public void queueThumbnail(T target, String url) {
+ Log.i(TAG, "Got a URL: " + url);
+
+ if (url == null) {
+ mRequestMap.remove(target);
+ } else {
+ mRequestMap.put(target, url);
+ mRequestHandler.obtainMessage(MESSAGE_DOWNLOAD, target)
+ .sendToTarget();
+ }
+ }
+
+ public void clearQueue() {
+ mRequestHandler.removeMessages(MESSAGE_DOWNLOAD);
+ mRequestMap.clear();
+ }
+
+ private void handleRequest(final T target) {
+ try {
+ final String url = mRequestMap.get(target);
+
+ if (url == null) {
+ return;
+ }
+
+ byte[] bitmapBytes = new FlickrFetchr().getUrlBytes(url);
+ final Bitmap bitmap = BitmapFactory
+ .decodeByteArray(bitmapBytes, 0, bitmapBytes.length);
+ Log.i(TAG, "Bitmap created");
+
+ mResponseHandler.post(new Runnable() {
+ public void run() {
+ if (mRequestMap.get(target) != url ||
+ mHasQuit) {
+ return;
+ }
+
+ mRequestMap.remove(target);
+ mThumbnailDownloadListener.onThumbnailDownloaded(target, bitmap);
+ }
+ });
+ } catch (IOException ioe) {
+ Log.e(TAG, "Error downloading image", ioe);
+ }
+ }
+}
diff --git a/Book Files/27_Search/PhotoGallery/app/src/main/res/drawable/bill_up_close.png b/Book Files/27_Search/PhotoGallery/app/src/main/res/drawable/bill_up_close.png
new file mode 100644
index 0000000..1e85792
Binary files /dev/null and b/Book Files/27_Search/PhotoGallery/app/src/main/res/drawable/bill_up_close.png differ
diff --git a/Book Files/27_Search/PhotoGallery/app/src/main/res/layout/activity_fragment.xml b/Book Files/27_Search/PhotoGallery/app/src/main/res/layout/activity_fragment.xml
new file mode 100644
index 0000000..1773143
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/app/src/main/res/layout/activity_fragment.xml
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/Book Files/27_Search/PhotoGallery/app/src/main/res/layout/fragment_photo_gallery.xml b/Book Files/27_Search/PhotoGallery/app/src/main/res/layout/fragment_photo_gallery.xml
new file mode 100644
index 0000000..4af4094
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/app/src/main/res/layout/fragment_photo_gallery.xml
@@ -0,0 +1,8 @@
+
+
diff --git a/Book Files/27_Search/PhotoGallery/app/src/main/res/layout/list_item_gallery.xml b/Book Files/27_Search/PhotoGallery/app/src/main/res/layout/list_item_gallery.xml
new file mode 100644
index 0000000..176311a
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/app/src/main/res/layout/list_item_gallery.xml
@@ -0,0 +1,7 @@
+
+
diff --git a/Book Files/27_Search/PhotoGallery/app/src/main/res/menu/fragment_photo_gallery.xml b/Book Files/27_Search/PhotoGallery/app/src/main/res/menu/fragment_photo_gallery.xml
new file mode 100644
index 0000000..438e757
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/app/src/main/res/menu/fragment_photo_gallery.xml
@@ -0,0 +1,13 @@
+
+
\ No newline at end of file
diff --git a/Book Files/27_Search/PhotoGallery/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Book Files/27_Search/PhotoGallery/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/Book Files/27_Search/PhotoGallery/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Book Files/27_Search/PhotoGallery/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Book Files/27_Search/PhotoGallery/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/Book Files/27_Search/PhotoGallery/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Book Files/27_Search/PhotoGallery/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Book Files/27_Search/PhotoGallery/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/Book Files/27_Search/PhotoGallery/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Book Files/27_Search/PhotoGallery/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Book Files/27_Search/PhotoGallery/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/Book Files/27_Search/PhotoGallery/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Book Files/27_Search/PhotoGallery/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Book Files/27_Search/PhotoGallery/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/Book Files/27_Search/PhotoGallery/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Book Files/27_Search/PhotoGallery/app/src/main/res/values-w820dp/dimens.xml b/Book Files/27_Search/PhotoGallery/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Book Files/27_Search/PhotoGallery/app/src/main/res/values/colors.xml b/Book Files/27_Search/PhotoGallery/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/Book Files/27_Search/PhotoGallery/app/src/main/res/values/dimens.xml b/Book Files/27_Search/PhotoGallery/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/Book Files/27_Search/PhotoGallery/app/src/main/res/values/strings.xml b/Book Files/27_Search/PhotoGallery/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..8c39166
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/app/src/main/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+ PhotoGallery
+ Search
+ Clear Search
+
diff --git a/Book Files/27_Search/PhotoGallery/app/src/main/res/values/styles.xml b/Book Files/27_Search/PhotoGallery/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Book Files/27_Search/PhotoGallery/app/src/test/java/com/bignerdranch/android/photogallery/ExampleUnitTest.java b/Book Files/27_Search/PhotoGallery/app/src/test/java/com/bignerdranch/android/photogallery/ExampleUnitTest.java
new file mode 100644
index 0000000..95126a2
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/app/src/test/java/com/bignerdranch/android/photogallery/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.photogallery;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/27_Search/PhotoGallery/build.gradle b/Book Files/27_Search/PhotoGallery/build.gradle
new file mode 100644
index 0000000..1ea4bd0
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Book Files/27_Search/PhotoGallery/gradle.properties b/Book Files/27_Search/PhotoGallery/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/Book Files/27_Search/PhotoGallery/gradle/wrapper/gradle-wrapper.jar b/Book Files/27_Search/PhotoGallery/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/Book Files/27_Search/PhotoGallery/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/27_Search/PhotoGallery/gradle/wrapper/gradle-wrapper.properties b/Book Files/27_Search/PhotoGallery/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a82392d
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/27_Search/PhotoGallery/gradlew b/Book Files/27_Search/PhotoGallery/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/27_Search/PhotoGallery/gradlew.bat b/Book Files/27_Search/PhotoGallery/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/27_Search/PhotoGallery/settings.gradle b/Book Files/27_Search/PhotoGallery/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/27_Search/PhotoGallery/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/28_IntentServices/LICENSE.txt b/Book Files/28_IntentServices/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Book Files/28_IntentServices/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Book Files/28_IntentServices/PhotoGallery/.gitignore b/Book Files/28_IntentServices/PhotoGallery/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Book Files/28_IntentServices/PhotoGallery/app/.gitignore b/Book Files/28_IntentServices/PhotoGallery/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/28_IntentServices/PhotoGallery/app/build.gradle b/Book Files/28_IntentServices/PhotoGallery/app/build.gradle
new file mode 100644
index 0000000..3f1ad7b
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/app/build.gradle
@@ -0,0 +1,30 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ applicationId "com.bignerdranch.android.photogallery"
+ minSdkVersion 19
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ testCompile 'junit:junit:4.12'
+ compile 'com.android.support:recyclerview-v7:25.3.0'
+}
diff --git a/Book Files/28_IntentServices/PhotoGallery/app/proguard-rules.pro b/Book Files/28_IntentServices/PhotoGallery/app/proguard-rules.pro
new file mode 100644
index 0000000..cf5610c
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/bphillips/devtools/android-sdk-macosx/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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 *;
+#}
diff --git a/Book Files/28_IntentServices/PhotoGallery/app/src/androidTest/java/com/bignerdranch/android/photogallery/ExampleInstrumentedTest.java b/Book Files/28_IntentServices/PhotoGallery/app/src/androidTest/java/com/bignerdranch/android/photogallery/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..409b32c
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/app/src/androidTest/java/com/bignerdranch/android/photogallery/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.photogallery;
+
+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.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.bignerdranch.android.photogallery", appContext.getPackageName());
+ }
+}
diff --git a/Book Files/28_IntentServices/PhotoGallery/app/src/main/AndroidManifest.xml b/Book Files/28_IntentServices/PhotoGallery/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..cc94dfd
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/app/src/main/AndroidManifest.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Book Files/28_IntentServices/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/FlickrFetchr.java b/Book Files/28_IntentServices/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/FlickrFetchr.java
new file mode 100644
index 0000000..55c2843
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/FlickrFetchr.java
@@ -0,0 +1,120 @@
+package com.bignerdranch.android.photogallery;
+
+import android.net.Uri;
+import android.util.Log;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+public class FlickrFetchr {
+ private static final String TAG = "FlickrFetchr";
+
+ private static final String API_KEY = "REPLACE_ME_WITH_A_REAL_KEY";
+ private static final String FETCH_RECENTS_METHOD = "flickr.photos.getRecent";
+ private static final String SEARCH_METHOD = "flickr.photos.search";
+ private static final Uri ENDPOINT = Uri
+ .parse("https://api.flickr.com/services/rest/")
+ .buildUpon()
+ .appendQueryParameter("api_key", API_KEY)
+ .appendQueryParameter("format", "json")
+ .appendQueryParameter("nojsoncallback", "1")
+ .appendQueryParameter("extras", "url_s")
+ .build();
+
+ public byte[] getUrlBytes(String urlSpec) throws IOException {
+ URL url = new URL(urlSpec);
+ HttpURLConnection connection = (HttpURLConnection)url.openConnection();
+ try {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ InputStream in = connection.getInputStream();
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ throw new IOException(connection.getResponseMessage() +
+ ": with " +
+ urlSpec);
+ }
+ int bytesRead = 0;
+ byte[] buffer = new byte[1024];
+ while ((bytesRead = in.read(buffer)) > 0) {
+ out.write(buffer, 0, bytesRead);
+ }
+ out.close();
+ return out.toByteArray();
+ } finally {
+ connection.disconnect();
+ }
+ }
+
+ public String getUrlString(String urlSpec) throws IOException {
+ return new String(getUrlBytes(urlSpec));
+ }
+
+ public List fetchRecentPhotos() {
+ String url = buildUrl(FETCH_RECENTS_METHOD, null);
+ return downloadGalleryItems(url);
+ }
+
+ public List searchPhotos(String query) {
+ String url = buildUrl(SEARCH_METHOD, query);
+ return downloadGalleryItems(url);
+ }
+
+ private List downloadGalleryItems(String url) {
+ List items = new ArrayList<>();
+
+ try {
+ String jsonString = getUrlString(url);
+ Log.i(TAG, "Received JSON: " + jsonString);
+ JSONObject jsonBody = new JSONObject(jsonString);
+ parseItems(items, jsonBody);
+ } catch (IOException ioe) {
+ Log.e(TAG, "Failed to fetch items", ioe);
+ } catch (JSONException je) {
+ Log.e(TAG, "Failed to parse JSON", je);
+ }
+
+ return items;
+ }
+
+ private String buildUrl(String method, String query) {
+ Uri.Builder uriBuilder = ENDPOINT.buildUpon()
+ .appendQueryParameter("method", method);
+
+ if (method.equals(SEARCH_METHOD)) {
+ uriBuilder.appendQueryParameter("text", query);
+ }
+
+ return uriBuilder.build().toString();
+ }
+
+ private void parseItems(List items, JSONObject jsonBody)
+ throws IOException, JSONException {
+
+ JSONObject photosJsonObject = jsonBody.getJSONObject("photos");
+ JSONArray photoJsonArray = photosJsonObject.getJSONArray("photo");
+
+ for (int i = 0; i < photoJsonArray.length(); i++) {
+ JSONObject photoJsonObject = photoJsonArray.getJSONObject(i);
+
+ GalleryItem item = new GalleryItem();
+ item.setId(photoJsonObject.getString("id"));
+ item.setCaption(photoJsonObject.getString("title"));
+
+ if (!photoJsonObject.has("url_s")) {
+ continue;
+ }
+
+ item.setUrl(photoJsonObject.getString("url_s"));
+ items.add(item);
+ }
+ }
+
+}
diff --git a/Book Files/28_IntentServices/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/GalleryItem.java b/Book Files/28_IntentServices/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/GalleryItem.java
new file mode 100644
index 0000000..a5a8d4b
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/GalleryItem.java
@@ -0,0 +1,36 @@
+package com.bignerdranch.android.photogallery;
+
+public class GalleryItem {
+ private String mCaption;
+ private String mId;
+ private String mUrl;
+
+ public String getCaption() {
+ return mCaption;
+ }
+
+ public void setCaption(String caption) {
+ mCaption = caption;
+ }
+
+ public String getId() {
+ return mId;
+ }
+
+ public void setId(String id) {
+ mId = id;
+ }
+
+ public String getUrl() {
+ return mUrl;
+ }
+
+ public void setUrl(String url) {
+ mUrl = url;
+ }
+
+ @Override
+ public String toString() {
+ return mCaption;
+ }
+}
diff --git a/Book Files/28_IntentServices/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryActivity.java b/Book Files/28_IntentServices/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryActivity.java
new file mode 100644
index 0000000..985b6ce
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryActivity.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.photogallery;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.v4.app.Fragment;
+
+public class PhotoGalleryActivity extends SingleFragmentActivity {
+
+ public static Intent newIntent(Context context) {
+ return new Intent(context, PhotoGalleryActivity.class);
+ }
+
+ @Override
+ protected Fragment createFragment() {
+ return PhotoGalleryFragment.newInstance();
+ }
+}
diff --git a/Book Files/28_IntentServices/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryFragment.java b/Book Files/28_IntentServices/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryFragment.java
new file mode 100644
index 0000000..9af3bc0
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryFragment.java
@@ -0,0 +1,222 @@
+package com.bignerdranch.android.photogallery;
+
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.SearchView;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PhotoGalleryFragment extends Fragment {
+ private static final String TAG = "PhotoGalleryFragment";
+
+ private RecyclerView mPhotoRecyclerView;
+ private List mItems = new ArrayList<>();
+ private ThumbnailDownloader mThumbnailDownloader;
+
+ public static PhotoGalleryFragment newInstance() {
+ return new PhotoGalleryFragment();
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setRetainInstance(true);
+ setHasOptionsMenu(true);
+
+ updateItems();
+
+ Handler responseHandler = new Handler();
+ mThumbnailDownloader = new ThumbnailDownloader<>(responseHandler);
+ mThumbnailDownloader.setThumbnailDownloadListener(
+ new ThumbnailDownloader.ThumbnailDownloadListener() {
+ @Override
+ public void onThumbnailDownloaded(PhotoHolder photoHolder, Bitmap bitmap) {
+ Drawable drawable = new BitmapDrawable(getResources(), bitmap);
+ photoHolder.bindDrawable(drawable);
+ }
+ }
+ );
+ mThumbnailDownloader.start();
+ mThumbnailDownloader.getLooper();
+ Log.i(TAG, "Background thread started");
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.fragment_photo_gallery, container, false);
+
+ mPhotoRecyclerView = (RecyclerView) v.findViewById(R.id.photo_recycler_view);
+ mPhotoRecyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 3));
+
+ setupAdapter();
+
+ return v;
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ mThumbnailDownloader.clearQueue();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mThumbnailDownloader.quit();
+ Log.i(TAG, "Background thread destroyed");
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
+ super.onCreateOptionsMenu(menu, menuInflater);
+ menuInflater.inflate(R.menu.fragment_photo_gallery, menu);
+
+ MenuItem searchItem = menu.findItem(R.id.menu_item_search);
+ final SearchView searchView = (SearchView) searchItem.getActionView();
+
+ searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ Log.d(TAG, "QueryTextSubmit: " + s);
+ QueryPreferences.setStoredQuery(getActivity(), s);
+ updateItems();
+ return true;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String s) {
+ Log.d(TAG, "QueryTextChange: " + s);
+ return false;
+ }
+ });
+
+ searchView.setOnSearchClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ String query = QueryPreferences.getStoredQuery(getActivity());
+ searchView.setQuery(query, false);
+ }
+ });
+
+ MenuItem toggleItem = menu.findItem(R.id.menu_item_toggle_polling);
+ if (PollService.isServiceAlarmOn(getActivity())) {
+ toggleItem.setTitle(R.string.stop_polling);
+ } else {
+ toggleItem.setTitle(R.string.start_polling);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_item_clear:
+ QueryPreferences.setStoredQuery(getActivity(), null);
+ updateItems();
+ return true;
+ case R.id.menu_item_toggle_polling:
+ boolean shouldStartAlarm = !PollService.isServiceAlarmOn(getActivity());
+ PollService.setServiceAlarm(getActivity(), shouldStartAlarm);
+ getActivity().invalidateOptionsMenu();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private void updateItems() {
+ String query = QueryPreferences.getStoredQuery(getActivity());
+ new FetchItemsTask(query).execute();
+ }
+
+ private void setupAdapter() {
+ if (isAdded()) {
+ mPhotoRecyclerView.setAdapter(new PhotoAdapter(mItems));
+ }
+ }
+
+ private class PhotoHolder extends RecyclerView.ViewHolder {
+ private ImageView mItemImageView;
+
+ public PhotoHolder(View itemView) {
+ super(itemView);
+
+ mItemImageView = (ImageView) itemView.findViewById(R.id.item_image_view);
+ }
+
+ public void bindDrawable(Drawable drawable) {
+ mItemImageView.setImageDrawable(drawable);
+ }
+ }
+
+ private class PhotoAdapter extends RecyclerView.Adapter {
+
+ private List mGalleryItems;
+
+ public PhotoAdapter(List galleryItems) {
+ mGalleryItems = galleryItems;
+ }
+
+ @Override
+ public PhotoHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
+ LayoutInflater inflater = LayoutInflater.from(getActivity());
+ View view = inflater.inflate(R.layout.list_item_gallery, viewGroup, false);
+ return new PhotoHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(PhotoHolder photoHolder, int position) {
+ GalleryItem galleryItem = mGalleryItems.get(position);
+ Drawable placeholder = getResources().getDrawable(R.drawable.bill_up_close);
+ photoHolder.bindDrawable(placeholder);
+ mThumbnailDownloader.queueThumbnail(photoHolder, galleryItem.getUrl());
+ }
+
+ @Override
+ public int getItemCount() {
+ return mGalleryItems.size();
+ }
+ }
+
+ private class FetchItemsTask extends AsyncTask> {
+ private String mQuery;
+
+ public FetchItemsTask(String query) {
+ mQuery = query;
+ }
+
+ @Override
+ protected List doInBackground(Void... params) {
+
+ if (mQuery == null) {
+ return new FlickrFetchr().fetchRecentPhotos();
+ } else {
+ return new FlickrFetchr().searchPhotos(mQuery);
+ }
+ }
+
+ @Override
+ protected void onPostExecute(List items) {
+ mItems = items;
+ setupAdapter();
+ }
+
+ }
+
+}
diff --git a/Book Files/28_IntentServices/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PollService.java b/Book Files/28_IntentServices/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PollService.java
new file mode 100644
index 0000000..a44a16c
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PollService.java
@@ -0,0 +1,115 @@
+package com.bignerdranch.android.photogallery;
+
+import android.app.AlarmManager;
+import android.app.IntentService;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.net.ConnectivityManager;
+import android.os.SystemClock;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.app.NotificationManagerCompat;
+import android.util.Log;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+public class PollService extends IntentService {
+ private static final String TAG = "PollService";
+
+ private static final long POLL_INTERVAL_MS = TimeUnit.MINUTES.toMillis(15);
+
+ public static Intent newIntent(Context context) {
+ return new Intent(context, PollService.class);
+ }
+
+ public static void setServiceAlarm(Context context, boolean isOn) {
+ Intent i = PollService.newIntent(context);
+ PendingIntent pi = PendingIntent.getService(
+ context, 0, i, 0);
+
+ AlarmManager alarmManager = (AlarmManager)
+ context.getSystemService(Context.ALARM_SERVICE);
+
+ if (isOn) {
+ alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME,
+ SystemClock.elapsedRealtime(), POLL_INTERVAL_MS, pi);
+ } else {
+ alarmManager.cancel(pi);
+ pi.cancel();
+ }
+ }
+
+ public static boolean isServiceAlarmOn(Context context) {
+ Intent i = PollService.newIntent(context);
+ PendingIntent pi = PendingIntent
+ .getService(context, 0, i, PendingIntent.FLAG_NO_CREATE);
+ return pi != null;
+ }
+
+ public PollService() {
+ super(TAG);
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+
+ if (!isNetworkAvailableAndConnected()) {
+ return;
+ }
+
+ String query = QueryPreferences.getStoredQuery(this);
+ String lastResultId = QueryPreferences.getLastResultId(this);
+ List items;
+
+ if (query == null) {
+ items = new FlickrFetchr().fetchRecentPhotos();
+ } else {
+ items = new FlickrFetchr().searchPhotos(query);
+ }
+
+ if (items.size() == 0) {
+ return;
+ }
+
+ String resultId = items.get(0).getId();
+ if (resultId.equals(lastResultId)) {
+ Log.i(TAG, "Got an old result: " + resultId);
+ } else {
+ Log.i(TAG, "Got a new result: " + resultId);
+
+ Resources resources = getResources();
+ Intent i = PhotoGalleryActivity.newIntent(this);
+ PendingIntent pi = PendingIntent
+ .getActivity(this, 0, i, 0);
+
+ Notification notification = new NotificationCompat.Builder(this)
+ .setTicker(resources.getString(R.string.new_pictures_title))
+ .setSmallIcon(android.R.drawable.ic_menu_report_image)
+ .setContentTitle(resources.getString(R.string.new_pictures_title))
+ .setContentText(resources.getString(R.string.new_pictures_text))
+ .setContentIntent(pi)
+ .setAutoCancel(true)
+ .build();
+
+ NotificationManagerCompat notificationManager =
+ NotificationManagerCompat.from(this);
+ notificationManager.notify(0, notification);
+ }
+
+ QueryPreferences.setLastResultId(this, resultId);
+ }
+
+ private boolean isNetworkAvailableAndConnected() {
+ ConnectivityManager cm =
+ (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
+
+ boolean isNetworkAvailable = cm.getActiveNetworkInfo() != null;
+ boolean isNetworkConnected = isNetworkAvailable &&
+ cm.getActiveNetworkInfo().isConnected();
+
+ return isNetworkConnected;
+ }
+}
diff --git a/Book Files/28_IntentServices/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/QueryPreferences.java b/Book Files/28_IntentServices/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/QueryPreferences.java
new file mode 100644
index 0000000..a10de09
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/QueryPreferences.java
@@ -0,0 +1,34 @@
+package com.bignerdranch.android.photogallery;
+
+import android.content.Context;
+import android.preference.PreferenceManager;
+
+public class QueryPreferences {
+
+ private static final String PREF_SEARCH_QUERY = "searchQuery";
+ private static final String PREF_LAST_RESULT_ID = "lastResultId";
+
+ public static String getStoredQuery(Context context) {
+ return PreferenceManager.getDefaultSharedPreferences(context)
+ .getString(PREF_SEARCH_QUERY, null);
+ }
+
+ public static void setStoredQuery(Context context, String query) {
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .putString(PREF_SEARCH_QUERY, query)
+ .apply();
+ }
+
+ public static String getLastResultId(Context context) {
+ return PreferenceManager.getDefaultSharedPreferences(context)
+ .getString(PREF_LAST_RESULT_ID, null);
+ }
+
+ public static void setLastResultId(Context context, String lastResultId) {
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .putString(PREF_LAST_RESULT_ID, lastResultId)
+ .apply();
+ }
+}
diff --git a/Book Files/28_IntentServices/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/SingleFragmentActivity.java b/Book Files/28_IntentServices/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/SingleFragmentActivity.java
new file mode 100644
index 0000000..faa98af
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/SingleFragmentActivity.java
@@ -0,0 +1,33 @@
+package com.bignerdranch.android.photogallery;
+
+import android.os.Bundle;
+import android.support.annotation.LayoutRes;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.AppCompatActivity;
+
+public abstract class SingleFragmentActivity extends AppCompatActivity {
+
+ protected abstract Fragment createFragment();
+
+ @LayoutRes
+ protected int getLayoutResId() {
+ return R.layout.activity_fragment;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(getLayoutResId());
+
+ FragmentManager fm = getSupportFragmentManager();
+ Fragment fragment = fm.findFragmentById(R.id.fragment_container);
+
+ if (fragment == null) {
+ fragment = createFragment();
+ fm.beginTransaction()
+ .add(R.id.fragment_container, fragment)
+ .commit();
+ }
+ }
+}
diff --git a/Book Files/28_IntentServices/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/ThumbnailDownloader.java b/Book Files/28_IntentServices/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/ThumbnailDownloader.java
new file mode 100644
index 0000000..d3fc81b
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/ThumbnailDownloader.java
@@ -0,0 +1,102 @@
+package com.bignerdranch.android.photogallery;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+public class ThumbnailDownloader extends HandlerThread {
+ private static final String TAG = "ThumbnailDownloader";
+ private static final int MESSAGE_DOWNLOAD = 0;
+
+ private boolean mHasQuit = false;
+ private Handler mRequestHandler;
+ private ConcurrentMap mRequestMap = new ConcurrentHashMap<>();
+ private Handler mResponseHandler;
+ private ThumbnailDownloadListener mThumbnailDownloadListener;
+
+ public interface ThumbnailDownloadListener {
+ void onThumbnailDownloaded(T target, Bitmap bitmap);
+ }
+
+ public void setThumbnailDownloadListener(ThumbnailDownloadListener listener) {
+ mThumbnailDownloadListener = listener;
+ }
+
+ public ThumbnailDownloader(Handler responseHandler) {
+ super(TAG);
+ mResponseHandler = responseHandler;
+ }
+
+ @Override
+ protected void onLooperPrepared() {
+ mRequestHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MESSAGE_DOWNLOAD) {
+ T target = (T) msg.obj;
+ Log.i(TAG, "Got a request for URL: " + mRequestMap.get(target));
+ handleRequest(target);
+ }
+ }
+ };
+ }
+
+ @Override
+ public boolean quit() {
+ mHasQuit = true;
+ return super.quit();
+ }
+
+ public void queueThumbnail(T target, String url) {
+ Log.i(TAG, "Got a URL: " + url);
+
+ if (url == null) {
+ mRequestMap.remove(target);
+ } else {
+ mRequestMap.put(target, url);
+ mRequestHandler.obtainMessage(MESSAGE_DOWNLOAD, target)
+ .sendToTarget();
+ }
+ }
+
+ public void clearQueue() {
+ mRequestHandler.removeMessages(MESSAGE_DOWNLOAD);
+ mRequestMap.clear();
+ }
+
+ private void handleRequest(final T target) {
+ try {
+ final String url = mRequestMap.get(target);
+
+ if (url == null) {
+ return;
+ }
+
+ byte[] bitmapBytes = new FlickrFetchr().getUrlBytes(url);
+ final Bitmap bitmap = BitmapFactory
+ .decodeByteArray(bitmapBytes, 0, bitmapBytes.length);
+ Log.i(TAG, "Bitmap created");
+
+ mResponseHandler.post(new Runnable() {
+ public void run() {
+ if (mRequestMap.get(target) != url ||
+ mHasQuit) {
+ return;
+ }
+
+ mRequestMap.remove(target);
+ mThumbnailDownloadListener.onThumbnailDownloaded(target, bitmap);
+ }
+ });
+ } catch (IOException ioe) {
+ Log.e(TAG, "Error downloading image", ioe);
+ }
+ }
+}
diff --git a/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/drawable/bill_up_close.png b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/drawable/bill_up_close.png
new file mode 100644
index 0000000..1e85792
Binary files /dev/null and b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/drawable/bill_up_close.png differ
diff --git a/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/layout/activity_fragment.xml b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/layout/activity_fragment.xml
new file mode 100644
index 0000000..1773143
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/layout/activity_fragment.xml
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/layout/fragment_photo_gallery.xml b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/layout/fragment_photo_gallery.xml
new file mode 100644
index 0000000..4af4094
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/layout/fragment_photo_gallery.xml
@@ -0,0 +1,8 @@
+
+
diff --git a/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/layout/list_item_gallery.xml b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/layout/list_item_gallery.xml
new file mode 100644
index 0000000..176311a
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/layout/list_item_gallery.xml
@@ -0,0 +1,7 @@
+
+
diff --git a/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/menu/fragment_photo_gallery.xml b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/menu/fragment_photo_gallery.xml
new file mode 100644
index 0000000..c31ba83
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/menu/fragment_photo_gallery.xml
@@ -0,0 +1,17 @@
+
+
\ No newline at end of file
diff --git a/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/values-w820dp/dimens.xml b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/values/colors.xml b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/values/dimens.xml b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/values/strings.xml b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..f087f6d
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/values/strings.xml
@@ -0,0 +1,9 @@
+
+ PhotoGallery
+ Search
+ Clear Search
+ Start polling
+ Stop polling
+ New PhotoGallery Pictures
+ You have new pictures in PhotoGallery.
+
diff --git a/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/values/styles.xml b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Book Files/28_IntentServices/PhotoGallery/app/src/test/java/com/bignerdranch/android/photogallery/ExampleUnitTest.java b/Book Files/28_IntentServices/PhotoGallery/app/src/test/java/com/bignerdranch/android/photogallery/ExampleUnitTest.java
new file mode 100644
index 0000000..95126a2
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/app/src/test/java/com/bignerdranch/android/photogallery/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.photogallery;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/28_IntentServices/PhotoGallery/build.gradle b/Book Files/28_IntentServices/PhotoGallery/build.gradle
new file mode 100644
index 0000000..1ea4bd0
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Book Files/28_IntentServices/PhotoGallery/gradle.properties b/Book Files/28_IntentServices/PhotoGallery/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/Book Files/28_IntentServices/PhotoGallery/gradle/wrapper/gradle-wrapper.jar b/Book Files/28_IntentServices/PhotoGallery/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/Book Files/28_IntentServices/PhotoGallery/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/28_IntentServices/PhotoGallery/gradle/wrapper/gradle-wrapper.properties b/Book Files/28_IntentServices/PhotoGallery/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a82392d
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/28_IntentServices/PhotoGallery/gradlew b/Book Files/28_IntentServices/PhotoGallery/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/28_IntentServices/PhotoGallery/gradlew.bat b/Book Files/28_IntentServices/PhotoGallery/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/28_IntentServices/PhotoGallery/settings.gradle b/Book Files/28_IntentServices/PhotoGallery/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/28_IntentServices/PhotoGallery/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/29_BroadcastIntents/.DS_Store b/Book Files/29_BroadcastIntents/.DS_Store
new file mode 100755
index 0000000..5b57649
Binary files /dev/null and b/Book Files/29_BroadcastIntents/.DS_Store differ
diff --git a/Book Files/29_BroadcastIntents/LICENSE.txt b/Book Files/29_BroadcastIntents/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/.gitignore b/Book Files/29_BroadcastIntents/PhotoGallery/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/.idea/compiler.xml b/Book Files/29_BroadcastIntents/PhotoGallery/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/.idea/copyright/profiles_settings.xml b/Book Files/29_BroadcastIntents/PhotoGallery/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/.idea/gradle.xml b/Book Files/29_BroadcastIntents/PhotoGallery/.idea/gradle.xml
new file mode 100644
index 0000000..7ac24c7
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/.idea/gradle.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/.idea/misc.xml b/Book Files/29_BroadcastIntents/PhotoGallery/.idea/misc.xml
new file mode 100644
index 0000000..b0a270f
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/.idea/misc.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/.idea/modules.xml b/Book Files/29_BroadcastIntents/PhotoGallery/.idea/modules.xml
new file mode 100644
index 0000000..9a0b686
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/.idea/runConfigurations.xml b/Book Files/29_BroadcastIntents/PhotoGallery/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/.gitignore b/Book Files/29_BroadcastIntents/PhotoGallery/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/build.gradle b/Book Files/29_BroadcastIntents/PhotoGallery/app/build.gradle
new file mode 100644
index 0000000..3f1ad7b
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/app/build.gradle
@@ -0,0 +1,30 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ applicationId "com.bignerdranch.android.photogallery"
+ minSdkVersion 19
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ testCompile 'junit:junit:4.12'
+ compile 'com.android.support:recyclerview-v7:25.3.0'
+}
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/proguard-rules.pro b/Book Files/29_BroadcastIntents/PhotoGallery/app/proguard-rules.pro
new file mode 100644
index 0000000..cf5610c
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/bphillips/devtools/android-sdk-macosx/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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 *;
+#}
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/src/androidTest/java/com/bignerdranch/android/photogallery/ExampleInstrumentedTest.java b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/androidTest/java/com/bignerdranch/android/photogallery/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..409b32c
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/androidTest/java/com/bignerdranch/android/photogallery/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.photogallery;
+
+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.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.bignerdranch.android.photogallery", appContext.getPackageName());
+ }
+}
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/AndroidManifest.xml b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..178198f
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/AndroidManifest.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/FlickrFetchr.java b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/FlickrFetchr.java
new file mode 100644
index 0000000..55c2843
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/FlickrFetchr.java
@@ -0,0 +1,120 @@
+package com.bignerdranch.android.photogallery;
+
+import android.net.Uri;
+import android.util.Log;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+public class FlickrFetchr {
+ private static final String TAG = "FlickrFetchr";
+
+ private static final String API_KEY = "REPLACE_ME_WITH_A_REAL_KEY";
+ private static final String FETCH_RECENTS_METHOD = "flickr.photos.getRecent";
+ private static final String SEARCH_METHOD = "flickr.photos.search";
+ private static final Uri ENDPOINT = Uri
+ .parse("https://api.flickr.com/services/rest/")
+ .buildUpon()
+ .appendQueryParameter("api_key", API_KEY)
+ .appendQueryParameter("format", "json")
+ .appendQueryParameter("nojsoncallback", "1")
+ .appendQueryParameter("extras", "url_s")
+ .build();
+
+ public byte[] getUrlBytes(String urlSpec) throws IOException {
+ URL url = new URL(urlSpec);
+ HttpURLConnection connection = (HttpURLConnection)url.openConnection();
+ try {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ InputStream in = connection.getInputStream();
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ throw new IOException(connection.getResponseMessage() +
+ ": with " +
+ urlSpec);
+ }
+ int bytesRead = 0;
+ byte[] buffer = new byte[1024];
+ while ((bytesRead = in.read(buffer)) > 0) {
+ out.write(buffer, 0, bytesRead);
+ }
+ out.close();
+ return out.toByteArray();
+ } finally {
+ connection.disconnect();
+ }
+ }
+
+ public String getUrlString(String urlSpec) throws IOException {
+ return new String(getUrlBytes(urlSpec));
+ }
+
+ public List fetchRecentPhotos() {
+ String url = buildUrl(FETCH_RECENTS_METHOD, null);
+ return downloadGalleryItems(url);
+ }
+
+ public List searchPhotos(String query) {
+ String url = buildUrl(SEARCH_METHOD, query);
+ return downloadGalleryItems(url);
+ }
+
+ private List downloadGalleryItems(String url) {
+ List items = new ArrayList<>();
+
+ try {
+ String jsonString = getUrlString(url);
+ Log.i(TAG, "Received JSON: " + jsonString);
+ JSONObject jsonBody = new JSONObject(jsonString);
+ parseItems(items, jsonBody);
+ } catch (IOException ioe) {
+ Log.e(TAG, "Failed to fetch items", ioe);
+ } catch (JSONException je) {
+ Log.e(TAG, "Failed to parse JSON", je);
+ }
+
+ return items;
+ }
+
+ private String buildUrl(String method, String query) {
+ Uri.Builder uriBuilder = ENDPOINT.buildUpon()
+ .appendQueryParameter("method", method);
+
+ if (method.equals(SEARCH_METHOD)) {
+ uriBuilder.appendQueryParameter("text", query);
+ }
+
+ return uriBuilder.build().toString();
+ }
+
+ private void parseItems(List items, JSONObject jsonBody)
+ throws IOException, JSONException {
+
+ JSONObject photosJsonObject = jsonBody.getJSONObject("photos");
+ JSONArray photoJsonArray = photosJsonObject.getJSONArray("photo");
+
+ for (int i = 0; i < photoJsonArray.length(); i++) {
+ JSONObject photoJsonObject = photoJsonArray.getJSONObject(i);
+
+ GalleryItem item = new GalleryItem();
+ item.setId(photoJsonObject.getString("id"));
+ item.setCaption(photoJsonObject.getString("title"));
+
+ if (!photoJsonObject.has("url_s")) {
+ continue;
+ }
+
+ item.setUrl(photoJsonObject.getString("url_s"));
+ items.add(item);
+ }
+ }
+
+}
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/GalleryItem.java b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/GalleryItem.java
new file mode 100644
index 0000000..a5a8d4b
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/GalleryItem.java
@@ -0,0 +1,36 @@
+package com.bignerdranch.android.photogallery;
+
+public class GalleryItem {
+ private String mCaption;
+ private String mId;
+ private String mUrl;
+
+ public String getCaption() {
+ return mCaption;
+ }
+
+ public void setCaption(String caption) {
+ mCaption = caption;
+ }
+
+ public String getId() {
+ return mId;
+ }
+
+ public void setId(String id) {
+ mId = id;
+ }
+
+ public String getUrl() {
+ return mUrl;
+ }
+
+ public void setUrl(String url) {
+ mUrl = url;
+ }
+
+ @Override
+ public String toString() {
+ return mCaption;
+ }
+}
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/NotificationReceiver.java b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/NotificationReceiver.java
new file mode 100644
index 0000000..1535eee
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/NotificationReceiver.java
@@ -0,0 +1,30 @@
+package com.bignerdranch.android.photogallery;
+
+import android.app.Activity;
+import android.app.Notification;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.support.v4.app.NotificationManagerCompat;
+import android.util.Log;
+
+public class NotificationReceiver extends BroadcastReceiver {
+ private static final String TAG = "NotificationReceiver";
+
+ @Override
+ public void onReceive(Context c, Intent i) {
+ Log.i(TAG, "received result: " + getResultCode());
+ if (getResultCode() != Activity.RESULT_OK) {
+ // A foreground activity cancelled the broadcast
+ return;
+ }
+
+ int requestCode = i.getIntExtra(PollService.REQUEST_CODE, 0);
+ Notification notification = (Notification)
+ i.getParcelableExtra(PollService.NOTIFICATION);
+
+ NotificationManagerCompat notificationManager =
+ NotificationManagerCompat.from(c);
+ notificationManager.notify(requestCode, notification);
+ }
+}
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryActivity.java b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryActivity.java
new file mode 100644
index 0000000..985b6ce
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryActivity.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.photogallery;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.v4.app.Fragment;
+
+public class PhotoGalleryActivity extends SingleFragmentActivity {
+
+ public static Intent newIntent(Context context) {
+ return new Intent(context, PhotoGalleryActivity.class);
+ }
+
+ @Override
+ protected Fragment createFragment() {
+ return PhotoGalleryFragment.newInstance();
+ }
+}
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryFragment.java b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryFragment.java
new file mode 100644
index 0000000..9d4dcd2
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryFragment.java
@@ -0,0 +1,222 @@
+package com.bignerdranch.android.photogallery;
+
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.SearchView;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PhotoGalleryFragment extends VisibleFragment {
+ private static final String TAG = "PhotoGalleryFragment";
+
+ private RecyclerView mPhotoRecyclerView;
+ private List mItems = new ArrayList<>();
+ private ThumbnailDownloader mThumbnailDownloader;
+
+ public static PhotoGalleryFragment newInstance() {
+ return new PhotoGalleryFragment();
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setRetainInstance(true);
+ setHasOptionsMenu(true);
+
+ updateItems();
+
+ Handler responseHandler = new Handler();
+ mThumbnailDownloader = new ThumbnailDownloader<>(responseHandler);
+ mThumbnailDownloader.setThumbnailDownloadListener(
+ new ThumbnailDownloader.ThumbnailDownloadListener() {
+ @Override
+ public void onThumbnailDownloaded(PhotoHolder photoHolder, Bitmap bitmap) {
+ Drawable drawable = new BitmapDrawable(getResources(), bitmap);
+ photoHolder.bindDrawable(drawable);
+ }
+ }
+ );
+ mThumbnailDownloader.start();
+ mThumbnailDownloader.getLooper();
+ Log.i(TAG, "Background thread started");
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.fragment_photo_gallery, container, false);
+
+ mPhotoRecyclerView = (RecyclerView) v.findViewById(R.id.photo_recycler_view);
+ mPhotoRecyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 3));
+
+ setupAdapter();
+
+ return v;
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ mThumbnailDownloader.clearQueue();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mThumbnailDownloader.quit();
+ Log.i(TAG, "Background thread destroyed");
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
+ super.onCreateOptionsMenu(menu, menuInflater);
+ menuInflater.inflate(R.menu.fragment_photo_gallery, menu);
+
+ MenuItem searchItem = menu.findItem(R.id.menu_item_search);
+ final SearchView searchView = (SearchView) searchItem.getActionView();
+
+ searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ Log.d(TAG, "QueryTextSubmit: " + s);
+ QueryPreferences.setStoredQuery(getActivity(), s);
+ updateItems();
+ return true;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String s) {
+ Log.d(TAG, "QueryTextChange: " + s);
+ return false;
+ }
+ });
+
+ searchView.setOnSearchClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ String query = QueryPreferences.getStoredQuery(getActivity());
+ searchView.setQuery(query, false);
+ }
+ });
+
+ MenuItem toggleItem = menu.findItem(R.id.menu_item_toggle_polling);
+ if (PollService.isServiceAlarmOn(getActivity())) {
+ toggleItem.setTitle(R.string.stop_polling);
+ } else {
+ toggleItem.setTitle(R.string.start_polling);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_item_clear:
+ QueryPreferences.setStoredQuery(getActivity(), null);
+ updateItems();
+ return true;
+ case R.id.menu_item_toggle_polling:
+ boolean shouldStartAlarm = !PollService.isServiceAlarmOn(getActivity());
+ PollService.setServiceAlarm(getActivity(), shouldStartAlarm);
+ getActivity().invalidateOptionsMenu();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private void updateItems() {
+ String query = QueryPreferences.getStoredQuery(getActivity());
+ new FetchItemsTask(query).execute();
+ }
+
+ private void setupAdapter() {
+ if (isAdded()) {
+ mPhotoRecyclerView.setAdapter(new PhotoAdapter(mItems));
+ }
+ }
+
+ private class PhotoHolder extends RecyclerView.ViewHolder {
+ private ImageView mItemImageView;
+
+ public PhotoHolder(View itemView) {
+ super(itemView);
+
+ mItemImageView = (ImageView) itemView.findViewById(R.id.item_image_view);
+ }
+
+ public void bindDrawable(Drawable drawable) {
+ mItemImageView.setImageDrawable(drawable);
+ }
+ }
+
+ private class PhotoAdapter extends RecyclerView.Adapter {
+
+ private List mGalleryItems;
+
+ public PhotoAdapter(List galleryItems) {
+ mGalleryItems = galleryItems;
+ }
+
+ @Override
+ public PhotoHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
+ LayoutInflater inflater = LayoutInflater.from(getActivity());
+ View view = inflater.inflate(R.layout.list_item_gallery, viewGroup, false);
+ return new PhotoHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(PhotoHolder photoHolder, int position) {
+ GalleryItem galleryItem = mGalleryItems.get(position);
+ Drawable placeholder = getResources().getDrawable(R.drawable.bill_up_close);
+ photoHolder.bindDrawable(placeholder);
+ mThumbnailDownloader.queueThumbnail(photoHolder, galleryItem.getUrl());
+ }
+
+ @Override
+ public int getItemCount() {
+ return mGalleryItems.size();
+ }
+ }
+
+ private class FetchItemsTask extends AsyncTask> {
+ private String mQuery;
+
+ public FetchItemsTask(String query) {
+ mQuery = query;
+ }
+
+ @Override
+ protected List doInBackground(Void... params) {
+
+ if (mQuery == null) {
+ return new FlickrFetchr().fetchRecentPhotos();
+ } else {
+ return new FlickrFetchr().searchPhotos(mQuery);
+ }
+ }
+
+ @Override
+ protected void onPostExecute(List items) {
+ mItems = items;
+ setupAdapter();
+ }
+
+ }
+
+}
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PollService.java b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PollService.java
new file mode 100644
index 0000000..4cd0a55
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PollService.java
@@ -0,0 +1,130 @@
+package com.bignerdranch.android.photogallery;
+
+import android.app.Activity;
+import android.app.AlarmManager;
+import android.app.IntentService;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.net.ConnectivityManager;
+import android.os.SystemClock;
+import android.support.v4.app.NotificationCompat;
+import android.util.Log;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+public class PollService extends IntentService {
+ private static final String TAG = "PollService";
+
+ private static final long POLL_INTERVAL_MS = TimeUnit.MINUTES.toMillis(15);
+
+ public static final String ACTION_SHOW_NOTIFICATION =
+ "com.bignerdranch.android.photogallery.SHOW_NOTIFICATION";
+ public static final String PERM_PRIVATE =
+ "com.bignerdranch.android.photogallery.PRIVATE";
+ public static final String REQUEST_CODE = "REQUEST_CODE";
+ public static final String NOTIFICATION = "NOTIFICATION";
+
+ public static Intent newIntent(Context context) {
+ return new Intent(context, PollService.class);
+ }
+
+ public static void setServiceAlarm(Context context, boolean isOn) {
+ Intent i = PollService.newIntent(context);
+ PendingIntent pi = PendingIntent.getService(
+ context, 0, i, 0);
+
+ AlarmManager alarmManager = (AlarmManager)
+ context.getSystemService(Context.ALARM_SERVICE);
+
+ if (isOn) {
+ alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME,
+ SystemClock.elapsedRealtime(), POLL_INTERVAL_MS, pi);
+ } else {
+ alarmManager.cancel(pi);
+ pi.cancel();
+ }
+
+ QueryPreferences.setAlarmOn(context, isOn);
+ }
+
+ public static boolean isServiceAlarmOn(Context context) {
+ Intent i = PollService.newIntent(context);
+ PendingIntent pi = PendingIntent
+ .getService(context, 0, i, PendingIntent.FLAG_NO_CREATE);
+ return pi != null;
+ }
+
+ public PollService() {
+ super(TAG);
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+
+ if (!isNetworkAvailableAndConnected()) {
+ return;
+ }
+
+ String query = QueryPreferences.getStoredQuery(this);
+ String lastResultId = QueryPreferences.getLastResultId(this);
+ List items;
+
+ if (query == null) {
+ items = new FlickrFetchr().fetchRecentPhotos();
+ } else {
+ items = new FlickrFetchr().searchPhotos(query);
+ }
+
+ if (items.size() == 0) {
+ return;
+ }
+
+ String resultId = items.get(0).getId();
+ if (resultId.equals(lastResultId)) {
+ Log.i(TAG, "Got an old result: " + resultId);
+ } else {
+ Log.i(TAG, "Got a new result: " + resultId);
+
+ Resources resources = getResources();
+ Intent i = PhotoGalleryActivity.newIntent(this);
+ PendingIntent pi = PendingIntent
+ .getActivity(this, 0, i, 0);
+
+ Notification notification = new NotificationCompat.Builder(this)
+ .setTicker(resources.getString(R.string.new_pictures_title))
+ .setSmallIcon(android.R.drawable.ic_menu_report_image)
+ .setContentTitle(resources.getString(R.string.new_pictures_title))
+ .setContentText(resources.getString(R.string.new_pictures_text))
+ .setContentIntent(pi)
+ .setAutoCancel(true)
+ .build();
+
+ showBackgroundNotification(0, notification);
+ }
+
+ QueryPreferences.setLastResultId(this, resultId);
+ }
+
+ private void showBackgroundNotification(int requestCode, Notification notification) {
+ Intent i = new Intent(ACTION_SHOW_NOTIFICATION);
+ i.putExtra(REQUEST_CODE, requestCode);
+ i.putExtra(NOTIFICATION, notification);
+ sendOrderedBroadcast(i, PERM_PRIVATE, null, null,
+ Activity.RESULT_OK, null, null);
+ }
+
+ private boolean isNetworkAvailableAndConnected() {
+ ConnectivityManager cm =
+ (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
+
+ boolean isNetworkAvailable = cm.getActiveNetworkInfo() != null;
+ boolean isNetworkConnected = isNetworkAvailable &&
+ cm.getActiveNetworkInfo().isConnected();
+
+ return isNetworkConnected;
+ }
+}
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/QueryPreferences.java b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/QueryPreferences.java
new file mode 100644
index 0000000..1b193a8
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/QueryPreferences.java
@@ -0,0 +1,48 @@
+package com.bignerdranch.android.photogallery;
+
+import android.content.Context;
+import android.preference.PreferenceManager;
+
+public class QueryPreferences {
+
+ private static final String PREF_SEARCH_QUERY = "searchQuery";
+ private static final String PREF_LAST_RESULT_ID = "lastResultId";
+ private static final String PREF_IS_ALARM_ON = "isAlarmOn";
+
+ public static String getStoredQuery(Context context) {
+ return PreferenceManager.getDefaultSharedPreferences(context)
+ .getString(PREF_SEARCH_QUERY, null);
+ }
+
+ public static void setStoredQuery(Context context, String query) {
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .putString(PREF_SEARCH_QUERY, query)
+ .apply();
+ }
+
+ public static String getLastResultId(Context context) {
+ return PreferenceManager.getDefaultSharedPreferences(context)
+ .getString(PREF_LAST_RESULT_ID, null);
+ }
+
+ public static void setLastResultId(Context context, String lastResultId) {
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .putString(PREF_LAST_RESULT_ID, lastResultId)
+ .apply();
+ }
+
+ public static boolean isAlarmOn(Context context) {
+ return PreferenceManager.getDefaultSharedPreferences(context)
+ .getBoolean(PREF_IS_ALARM_ON, false);
+ }
+
+ public static void setAlarmOn(Context context, boolean isOn) {
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .putBoolean(PREF_IS_ALARM_ON, isOn)
+ .apply();
+ }
+
+}
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/SingleFragmentActivity.java b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/SingleFragmentActivity.java
new file mode 100644
index 0000000..faa98af
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/SingleFragmentActivity.java
@@ -0,0 +1,33 @@
+package com.bignerdranch.android.photogallery;
+
+import android.os.Bundle;
+import android.support.annotation.LayoutRes;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.AppCompatActivity;
+
+public abstract class SingleFragmentActivity extends AppCompatActivity {
+
+ protected abstract Fragment createFragment();
+
+ @LayoutRes
+ protected int getLayoutResId() {
+ return R.layout.activity_fragment;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(getLayoutResId());
+
+ FragmentManager fm = getSupportFragmentManager();
+ Fragment fragment = fm.findFragmentById(R.id.fragment_container);
+
+ if (fragment == null) {
+ fragment = createFragment();
+ fm.beginTransaction()
+ .add(R.id.fragment_container, fragment)
+ .commit();
+ }
+ }
+}
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/StartupReceiver.java b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/StartupReceiver.java
new file mode 100644
index 0000000..804f076
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/StartupReceiver.java
@@ -0,0 +1,19 @@
+package com.bignerdranch.android.photogallery;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public class StartupReceiver extends BroadcastReceiver{
+ private static final String TAG = "StartupReceiver";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.i(TAG, "Received broadcast intent: " + intent.getAction());
+
+ boolean isOn = QueryPreferences.isAlarmOn(context);
+ PollService.setServiceAlarm(context, isOn);
+ }
+
+}
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/ThumbnailDownloader.java b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/ThumbnailDownloader.java
new file mode 100644
index 0000000..d3fc81b
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/ThumbnailDownloader.java
@@ -0,0 +1,102 @@
+package com.bignerdranch.android.photogallery;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+public class ThumbnailDownloader extends HandlerThread {
+ private static final String TAG = "ThumbnailDownloader";
+ private static final int MESSAGE_DOWNLOAD = 0;
+
+ private boolean mHasQuit = false;
+ private Handler mRequestHandler;
+ private ConcurrentMap mRequestMap = new ConcurrentHashMap<>();
+ private Handler mResponseHandler;
+ private ThumbnailDownloadListener mThumbnailDownloadListener;
+
+ public interface ThumbnailDownloadListener {
+ void onThumbnailDownloaded(T target, Bitmap bitmap);
+ }
+
+ public void setThumbnailDownloadListener(ThumbnailDownloadListener listener) {
+ mThumbnailDownloadListener = listener;
+ }
+
+ public ThumbnailDownloader(Handler responseHandler) {
+ super(TAG);
+ mResponseHandler = responseHandler;
+ }
+
+ @Override
+ protected void onLooperPrepared() {
+ mRequestHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MESSAGE_DOWNLOAD) {
+ T target = (T) msg.obj;
+ Log.i(TAG, "Got a request for URL: " + mRequestMap.get(target));
+ handleRequest(target);
+ }
+ }
+ };
+ }
+
+ @Override
+ public boolean quit() {
+ mHasQuit = true;
+ return super.quit();
+ }
+
+ public void queueThumbnail(T target, String url) {
+ Log.i(TAG, "Got a URL: " + url);
+
+ if (url == null) {
+ mRequestMap.remove(target);
+ } else {
+ mRequestMap.put(target, url);
+ mRequestHandler.obtainMessage(MESSAGE_DOWNLOAD, target)
+ .sendToTarget();
+ }
+ }
+
+ public void clearQueue() {
+ mRequestHandler.removeMessages(MESSAGE_DOWNLOAD);
+ mRequestMap.clear();
+ }
+
+ private void handleRequest(final T target) {
+ try {
+ final String url = mRequestMap.get(target);
+
+ if (url == null) {
+ return;
+ }
+
+ byte[] bitmapBytes = new FlickrFetchr().getUrlBytes(url);
+ final Bitmap bitmap = BitmapFactory
+ .decodeByteArray(bitmapBytes, 0, bitmapBytes.length);
+ Log.i(TAG, "Bitmap created");
+
+ mResponseHandler.post(new Runnable() {
+ public void run() {
+ if (mRequestMap.get(target) != url ||
+ mHasQuit) {
+ return;
+ }
+
+ mRequestMap.remove(target);
+ mThumbnailDownloadListener.onThumbnailDownloaded(target, bitmap);
+ }
+ });
+ } catch (IOException ioe) {
+ Log.e(TAG, "Error downloading image", ioe);
+ }
+ }
+}
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/VisibleFragment.java b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/VisibleFragment.java
new file mode 100644
index 0000000..fc0a379
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/VisibleFragment.java
@@ -0,0 +1,38 @@
+package com.bignerdranch.android.photogallery;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.support.v4.app.Fragment;
+import android.util.Log;
+import android.widget.Toast;
+
+public abstract class VisibleFragment extends Fragment {
+ private static final String TAG = "VisibleFragment";
+
+ private BroadcastReceiver mOnShowNotification = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // If we receive this, we're visible, so cancel
+ // the notification
+ Log.i(TAG, "canceling notification");
+ setResultCode(Activity.RESULT_CANCELED);
+ }
+ };
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ IntentFilter filter = new IntentFilter(PollService.ACTION_SHOW_NOTIFICATION);
+ getActivity().registerReceiver(mOnShowNotification, filter,
+ PollService.PERM_PRIVATE, null);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ getActivity().unregisterReceiver(mOnShowNotification);
+ }
+}
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/drawable/bill_up_close.png b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/drawable/bill_up_close.png
new file mode 100644
index 0000000..1e85792
Binary files /dev/null and b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/drawable/bill_up_close.png differ
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/layout/activity_fragment.xml b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/layout/activity_fragment.xml
new file mode 100644
index 0000000..1773143
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/layout/activity_fragment.xml
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/layout/fragment_photo_gallery.xml b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/layout/fragment_photo_gallery.xml
new file mode 100644
index 0000000..4af4094
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/layout/fragment_photo_gallery.xml
@@ -0,0 +1,8 @@
+
+
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/layout/list_item_gallery.xml b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/layout/list_item_gallery.xml
new file mode 100644
index 0000000..176311a
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/layout/list_item_gallery.xml
@@ -0,0 +1,7 @@
+
+
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/menu/fragment_photo_gallery.xml b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/menu/fragment_photo_gallery.xml
new file mode 100644
index 0000000..c31ba83
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/menu/fragment_photo_gallery.xml
@@ -0,0 +1,17 @@
+
+
\ No newline at end of file
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/values-w820dp/dimens.xml b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/values/colors.xml b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/values/dimens.xml b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/values/strings.xml b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..f087f6d
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/values/strings.xml
@@ -0,0 +1,9 @@
+
+ PhotoGallery
+ Search
+ Clear Search
+ Start polling
+ Stop polling
+ New PhotoGallery Pictures
+ You have new pictures in PhotoGallery.
+
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/values/styles.xml b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/app/src/test/java/com/bignerdranch/android/photogallery/ExampleUnitTest.java b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/test/java/com/bignerdranch/android/photogallery/ExampleUnitTest.java
new file mode 100644
index 0000000..95126a2
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/app/src/test/java/com/bignerdranch/android/photogallery/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.photogallery;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/build.gradle b/Book Files/29_BroadcastIntents/PhotoGallery/build.gradle
new file mode 100644
index 0000000..1ea4bd0
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/gradle.properties b/Book Files/29_BroadcastIntents/PhotoGallery/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/gradle/wrapper/gradle-wrapper.jar b/Book Files/29_BroadcastIntents/PhotoGallery/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/Book Files/29_BroadcastIntents/PhotoGallery/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/gradle/wrapper/gradle-wrapper.properties b/Book Files/29_BroadcastIntents/PhotoGallery/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a82392d
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/gradlew b/Book Files/29_BroadcastIntents/PhotoGallery/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/gradlew.bat b/Book Files/29_BroadcastIntents/PhotoGallery/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/29_BroadcastIntents/PhotoGallery/settings.gradle b/Book Files/29_BroadcastIntents/PhotoGallery/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/29_BroadcastIntents/PhotoGallery/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/30_WebView/.DS_Store b/Book Files/30_WebView/.DS_Store
new file mode 100755
index 0000000..8d11cbb
Binary files /dev/null and b/Book Files/30_WebView/.DS_Store differ
diff --git a/Book Files/30_WebView/LICENSE.txt b/Book Files/30_WebView/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Book Files/30_WebView/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Book Files/30_WebView/PhotoGallery/.gitignore b/Book Files/30_WebView/PhotoGallery/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Book Files/30_WebView/PhotoGallery/.idea/compiler.xml b/Book Files/30_WebView/PhotoGallery/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/30_WebView/PhotoGallery/.idea/copyright/profiles_settings.xml b/Book Files/30_WebView/PhotoGallery/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Book Files/30_WebView/PhotoGallery/.idea/encodings.xml b/Book Files/30_WebView/PhotoGallery/.idea/encodings.xml
new file mode 100644
index 0000000..97626ba
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/30_WebView/PhotoGallery/.idea/gradle.xml b/Book Files/30_WebView/PhotoGallery/.idea/gradle.xml
new file mode 100644
index 0000000..0e23f8e
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/.idea/gradle.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/30_WebView/PhotoGallery/.idea/misc.xml b/Book Files/30_WebView/PhotoGallery/.idea/misc.xml
new file mode 100644
index 0000000..5d19981
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/.idea/misc.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/30_WebView/PhotoGallery/.idea/modules.xml b/Book Files/30_WebView/PhotoGallery/.idea/modules.xml
new file mode 100644
index 0000000..9a0b686
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/30_WebView/PhotoGallery/.idea/runConfigurations.xml b/Book Files/30_WebView/PhotoGallery/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/30_WebView/PhotoGallery/app/.gitignore b/Book Files/30_WebView/PhotoGallery/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/30_WebView/PhotoGallery/app/build.gradle b/Book Files/30_WebView/PhotoGallery/app/build.gradle
new file mode 100644
index 0000000..b8dd708
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/app/build.gradle
@@ -0,0 +1,31 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ applicationId "com.bignerdranch.android.photogallery"
+ minSdkVersion 19
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ testCompile 'junit:junit:4.12'
+ compile 'com.android.support:recyclerview-v7:25.3.0'
+ compile 'com.android.support.constraint:constraint-layout:1.0.2'
+}
diff --git a/Book Files/30_WebView/PhotoGallery/app/proguard-rules.pro b/Book Files/30_WebView/PhotoGallery/app/proguard-rules.pro
new file mode 100644
index 0000000..cf5610c
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/bphillips/devtools/android-sdk-macosx/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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 *;
+#}
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/androidTest/java/com/bignerdranch/android/photogallery/ExampleInstrumentedTest.java b/Book Files/30_WebView/PhotoGallery/app/src/androidTest/java/com/bignerdranch/android/photogallery/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..409b32c
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/app/src/androidTest/java/com/bignerdranch/android/photogallery/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.photogallery;
+
+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.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.bignerdranch.android.photogallery", appContext.getPackageName());
+ }
+}
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/AndroidManifest.xml b/Book Files/30_WebView/PhotoGallery/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..334d04c
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/app/src/main/AndroidManifest.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/FlickrFetchr.java b/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/FlickrFetchr.java
new file mode 100644
index 0000000..c519edd
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/FlickrFetchr.java
@@ -0,0 +1,121 @@
+package com.bignerdranch.android.photogallery;
+
+import android.net.Uri;
+import android.util.Log;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+public class FlickrFetchr {
+ private static final String TAG = "FlickrFetchr";
+
+ private static final String API_KEY = "REPLACE_ME_WITH_A_REAL_KEY";
+ private static final String FETCH_RECENTS_METHOD = "flickr.photos.getRecent";
+ private static final String SEARCH_METHOD = "flickr.photos.search";
+ private static final Uri ENDPOINT = Uri
+ .parse("https://api.flickr.com/services/rest/")
+ .buildUpon()
+ .appendQueryParameter("api_key", API_KEY)
+ .appendQueryParameter("format", "json")
+ .appendQueryParameter("nojsoncallback", "1")
+ .appendQueryParameter("extras", "url_s")
+ .build();
+
+ public byte[] getUrlBytes(String urlSpec) throws IOException {
+ URL url = new URL(urlSpec);
+ HttpURLConnection connection = (HttpURLConnection)url.openConnection();
+ try {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ InputStream in = connection.getInputStream();
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ throw new IOException(connection.getResponseMessage() +
+ ": with " +
+ urlSpec);
+ }
+ int bytesRead = 0;
+ byte[] buffer = new byte[1024];
+ while ((bytesRead = in.read(buffer)) > 0) {
+ out.write(buffer, 0, bytesRead);
+ }
+ out.close();
+ return out.toByteArray();
+ } finally {
+ connection.disconnect();
+ }
+ }
+
+ public String getUrlString(String urlSpec) throws IOException {
+ return new String(getUrlBytes(urlSpec));
+ }
+
+ public List fetchRecentPhotos() {
+ String url = buildUrl(FETCH_RECENTS_METHOD, null);
+ return downloadGalleryItems(url);
+ }
+
+ public List searchPhotos(String query) {
+ String url = buildUrl(SEARCH_METHOD, query);
+ return downloadGalleryItems(url);
+ }
+
+ private List downloadGalleryItems(String url) {
+ List items = new ArrayList<>();
+
+ try {
+ String jsonString = getUrlString(url);
+ Log.i(TAG, "Received JSON: " + jsonString);
+ JSONObject jsonBody = new JSONObject(jsonString);
+ parseItems(items, jsonBody);
+ } catch (IOException ioe) {
+ Log.e(TAG, "Failed to fetch items", ioe);
+ } catch (JSONException je) {
+ Log.e(TAG, "Failed to parse JSON", je);
+ }
+
+ return items;
+ }
+
+ private String buildUrl(String method, String query) {
+ Uri.Builder uriBuilder = ENDPOINT.buildUpon()
+ .appendQueryParameter("method", method);
+
+ if (method.equals(SEARCH_METHOD)) {
+ uriBuilder.appendQueryParameter("text", query);
+ }
+
+ return uriBuilder.build().toString();
+ }
+
+ private void parseItems(List items, JSONObject jsonBody)
+ throws IOException, JSONException {
+
+ JSONObject photosJsonObject = jsonBody.getJSONObject("photos");
+ JSONArray photoJsonArray = photosJsonObject.getJSONArray("photo");
+
+ for (int i = 0; i < photoJsonArray.length(); i++) {
+ JSONObject photoJsonObject = photoJsonArray.getJSONObject(i);
+
+ GalleryItem item = new GalleryItem();
+ item.setId(photoJsonObject.getString("id"));
+ item.setCaption(photoJsonObject.getString("title"));
+
+ if (!photoJsonObject.has("url_s")) {
+ continue;
+ }
+
+ item.setUrl(photoJsonObject.getString("url_s"));
+ item.setOwner(photoJsonObject.getString("owner"));
+ items.add(item);
+ }
+ }
+
+}
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/GalleryItem.java b/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/GalleryItem.java
new file mode 100644
index 0000000..e333708
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/GalleryItem.java
@@ -0,0 +1,55 @@
+package com.bignerdranch.android.photogallery;
+
+import android.net.Uri;
+
+public class GalleryItem {
+ private String mCaption;
+ private String mId;
+ private String mUrl;
+ private String mOwner;
+
+ public String getCaption() {
+ return mCaption;
+ }
+
+ public void setCaption(String caption) {
+ mCaption = caption;
+ }
+
+ public String getId() {
+ return mId;
+ }
+
+ public void setId(String id) {
+ mId = id;
+ }
+
+ public String getUrl() {
+ return mUrl;
+ }
+
+ public void setUrl(String url) {
+ mUrl = url;
+ }
+
+ public String getOwner() {
+ return mOwner;
+ }
+
+ public void setOwner(String owner) {
+ mOwner = owner;
+ }
+
+ public Uri getPhotoPageUri() {
+ return Uri.parse("http://www.flickr.com/photos/")
+ .buildUpon()
+ .appendPath(mOwner)
+ .appendPath(mId)
+ .build();
+ }
+
+ @Override
+ public String toString() {
+ return mCaption;
+ }
+}
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/NotificationReceiver.java b/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/NotificationReceiver.java
new file mode 100644
index 0000000..1535eee
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/NotificationReceiver.java
@@ -0,0 +1,30 @@
+package com.bignerdranch.android.photogallery;
+
+import android.app.Activity;
+import android.app.Notification;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.support.v4.app.NotificationManagerCompat;
+import android.util.Log;
+
+public class NotificationReceiver extends BroadcastReceiver {
+ private static final String TAG = "NotificationReceiver";
+
+ @Override
+ public void onReceive(Context c, Intent i) {
+ Log.i(TAG, "received result: " + getResultCode());
+ if (getResultCode() != Activity.RESULT_OK) {
+ // A foreground activity cancelled the broadcast
+ return;
+ }
+
+ int requestCode = i.getIntExtra(PollService.REQUEST_CODE, 0);
+ Notification notification = (Notification)
+ i.getParcelableExtra(PollService.NOTIFICATION);
+
+ NotificationManagerCompat notificationManager =
+ NotificationManagerCompat.from(c);
+ notificationManager.notify(requestCode, notification);
+ }
+}
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryActivity.java b/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryActivity.java
new file mode 100644
index 0000000..985b6ce
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryActivity.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.photogallery;
+
+import android.content.Context;
+import android.content.Intent;
+import android.support.v4.app.Fragment;
+
+public class PhotoGalleryActivity extends SingleFragmentActivity {
+
+ public static Intent newIntent(Context context) {
+ return new Intent(context, PhotoGalleryActivity.class);
+ }
+
+ @Override
+ protected Fragment createFragment() {
+ return PhotoGalleryFragment.newInstance();
+ }
+}
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryFragment.java b/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryFragment.java
new file mode 100644
index 0000000..63e9e06
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoGalleryFragment.java
@@ -0,0 +1,239 @@
+package com.bignerdranch.android.photogallery;
+
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.app.Fragment;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.SearchView;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class PhotoGalleryFragment extends VisibleFragment {
+ private static final String TAG = "PhotoGalleryFragment";
+
+ private RecyclerView mPhotoRecyclerView;
+ private List mItems = new ArrayList<>();
+ private ThumbnailDownloader mThumbnailDownloader;
+
+ public static PhotoGalleryFragment newInstance() {
+ return new PhotoGalleryFragment();
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setRetainInstance(true);
+ setHasOptionsMenu(true);
+
+ updateItems();
+
+ Handler responseHandler = new Handler();
+ mThumbnailDownloader = new ThumbnailDownloader<>(responseHandler);
+ mThumbnailDownloader.setThumbnailDownloadListener(
+ new ThumbnailDownloader.ThumbnailDownloadListener() {
+ @Override
+ public void onThumbnailDownloaded(PhotoHolder photoHolder, Bitmap bitmap) {
+ Drawable drawable = new BitmapDrawable(getResources(), bitmap);
+ photoHolder.bindDrawable(drawable);
+ }
+ }
+ );
+ mThumbnailDownloader.start();
+ mThumbnailDownloader.getLooper();
+ Log.i(TAG, "Background thread started");
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.fragment_photo_gallery, container, false);
+
+ mPhotoRecyclerView = (RecyclerView) v.findViewById(R.id.photo_recycler_view);
+ mPhotoRecyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 3));
+
+ setupAdapter();
+
+ return v;
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ mThumbnailDownloader.clearQueue();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mThumbnailDownloader.quit();
+ Log.i(TAG, "Background thread destroyed");
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
+ super.onCreateOptionsMenu(menu, menuInflater);
+ menuInflater.inflate(R.menu.fragment_photo_gallery, menu);
+
+ MenuItem searchItem = menu.findItem(R.id.menu_item_search);
+ final SearchView searchView = (SearchView) searchItem.getActionView();
+
+ searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String s) {
+ Log.d(TAG, "QueryTextSubmit: " + s);
+ QueryPreferences.setStoredQuery(getActivity(), s);
+ updateItems();
+ return true;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String s) {
+ Log.d(TAG, "QueryTextChange: " + s);
+ return false;
+ }
+ });
+
+ searchView.setOnSearchClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ String query = QueryPreferences.getStoredQuery(getActivity());
+ searchView.setQuery(query, false);
+ }
+ });
+
+ MenuItem toggleItem = menu.findItem(R.id.menu_item_toggle_polling);
+ if (PollService.isServiceAlarmOn(getActivity())) {
+ toggleItem.setTitle(R.string.stop_polling);
+ } else {
+ toggleItem.setTitle(R.string.start_polling);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_item_clear:
+ QueryPreferences.setStoredQuery(getActivity(), null);
+ updateItems();
+ return true;
+ case R.id.menu_item_toggle_polling:
+ boolean shouldStartAlarm = !PollService.isServiceAlarmOn(getActivity());
+ PollService.setServiceAlarm(getActivity(), shouldStartAlarm);
+ getActivity().invalidateOptionsMenu();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private void updateItems() {
+ String query = QueryPreferences.getStoredQuery(getActivity());
+ new FetchItemsTask(query).execute();
+ }
+
+ private void setupAdapter() {
+ if (isAdded()) {
+ mPhotoRecyclerView.setAdapter(new PhotoAdapter(mItems));
+ }
+ }
+
+ private class PhotoHolder extends RecyclerView.ViewHolder
+ implements View.OnClickListener{
+ private ImageView mItemImageView;
+ private GalleryItem mGalleryItem;
+
+ public PhotoHolder(View itemView) {
+ super(itemView);
+
+ mItemImageView = (ImageView) itemView.findViewById(R.id.item_image_view);
+ itemView.setOnClickListener(this);
+ }
+
+ public void bindDrawable(Drawable drawable) {
+ mItemImageView.setImageDrawable(drawable);
+ }
+
+ public void bindGalleryItem(GalleryItem galleryItem) {
+ mGalleryItem = galleryItem;
+ }
+
+ @Override
+ public void onClick(View v) {
+ Intent i = PhotoPageActivity
+ .newIntent(getActivity(), mGalleryItem.getPhotoPageUri());
+ startActivity(i);
+ }
+ }
+
+ private class PhotoAdapter extends RecyclerView.Adapter {
+
+ private List mGalleryItems;
+
+ public PhotoAdapter(List galleryItems) {
+ mGalleryItems = galleryItems;
+ }
+
+ @Override
+ public PhotoHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
+ LayoutInflater inflater = LayoutInflater.from(getActivity());
+ View view = inflater.inflate(R.layout.list_item_gallery, viewGroup, false);
+ return new PhotoHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(PhotoHolder photoHolder, int position) {
+ GalleryItem galleryItem = mGalleryItems.get(position);
+ photoHolder.bindGalleryItem(galleryItem);
+ Drawable placeholder = getResources().getDrawable(R.drawable.bill_up_close);
+ photoHolder.bindDrawable(placeholder);
+ mThumbnailDownloader.queueThumbnail(photoHolder, galleryItem.getUrl());
+ }
+
+ @Override
+ public int getItemCount() {
+ return mGalleryItems.size();
+ }
+ }
+
+ private class FetchItemsTask extends AsyncTask> {
+ private String mQuery;
+
+ public FetchItemsTask(String query) {
+ mQuery = query;
+ }
+
+ @Override
+ protected List doInBackground(Void... params) {
+
+ if (mQuery == null) {
+ return new FlickrFetchr().fetchRecentPhotos();
+ } else {
+ return new FlickrFetchr().searchPhotos(mQuery);
+ }
+ }
+
+ @Override
+ protected void onPostExecute(List items) {
+ mItems = items;
+ setupAdapter();
+ }
+
+ }
+
+}
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoPageActivity.java b/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoPageActivity.java
new file mode 100644
index 0000000..185f4a1
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoPageActivity.java
@@ -0,0 +1,20 @@
+package com.bignerdranch.android.photogallery;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.support.v4.app.Fragment;
+
+public class PhotoPageActivity extends SingleFragmentActivity {
+
+ public static Intent newIntent(Context context, Uri photoPageUri) {
+ Intent i = new Intent(context, PhotoPageActivity.class);
+ i.setData(photoPageUri);
+ return i;
+ }
+
+ @Override
+ protected Fragment createFragment() {
+ return PhotoPageFragment.newInstance(getIntent().getData());
+ }
+}
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoPageFragment.java b/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoPageFragment.java
new file mode 100644
index 0000000..dd48924
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PhotoPageFragment.java
@@ -0,0 +1,67 @@
+package com.bignerdranch.android.photogallery;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.webkit.WebChromeClient;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.ProgressBar;
+
+public class PhotoPageFragment extends VisibleFragment {
+ private static final String ARG_URI = "photo_page_url";
+
+ private Uri mUri;
+ private WebView mWebView;
+ private ProgressBar mProgressBar;
+
+ public static PhotoPageFragment newInstance(Uri uri) {
+ Bundle args = new Bundle();
+ args.putParcelable(ARG_URI, uri);
+
+ PhotoPageFragment fragment = new PhotoPageFragment();
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mUri = getArguments().getParcelable(ARG_URI);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.fragment_photo_page, container, false);
+
+ mProgressBar = (ProgressBar)v.findViewById(R.id.progress_bar);
+ mProgressBar.setMax(100); // WebChromeClient reports in range 0-100
+
+ mWebView = (WebView) v.findViewById(R.id.web_view);
+ mWebView.getSettings().setJavaScriptEnabled(true);
+ mWebView.setWebChromeClient(new WebChromeClient() {
+ public void onProgressChanged(WebView webView, int newProgress) {
+ if (newProgress == 100) {
+ mProgressBar.setVisibility(View.GONE);
+ } else {
+ mProgressBar.setVisibility(View.VISIBLE);
+ mProgressBar.setProgress(newProgress);
+ }
+ }
+
+ public void onReceivedTitle(WebView webView, String title) {
+ AppCompatActivity activity = (AppCompatActivity) getActivity();
+ activity.getSupportActionBar().setSubtitle(title);
+ }
+ });
+ mWebView.setWebViewClient(new WebViewClient());
+ mWebView.loadUrl(mUri.toString());
+
+ return v;
+ }
+}
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PollService.java b/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PollService.java
new file mode 100644
index 0000000..884e086
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/PollService.java
@@ -0,0 +1,131 @@
+package com.bignerdranch.android.photogallery;
+
+import android.app.Activity;
+import android.app.AlarmManager;
+import android.app.IntentService;
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.net.ConnectivityManager;
+import android.os.SystemClock;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.app.NotificationManagerCompat;
+import android.util.Log;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+public class PollService extends IntentService {
+ private static final String TAG = "PollService";
+
+ private static final long POLL_INTERVAL_MS = TimeUnit.MINUTES.toMillis(15);
+
+ public static final String ACTION_SHOW_NOTIFICATION =
+ "com.bignerdranch.android.photogallery.SHOW_NOTIFICATION";
+ public static final String PERM_PRIVATE =
+ "com.bignerdranch.android.photogallery.PRIVATE";
+ public static final String REQUEST_CODE = "REQUEST_CODE";
+ public static final String NOTIFICATION = "NOTIFICATION";
+
+ public static Intent newIntent(Context context) {
+ return new Intent(context, PollService.class);
+ }
+
+ public static void setServiceAlarm(Context context, boolean isOn) {
+ Intent i = PollService.newIntent(context);
+ PendingIntent pi = PendingIntent.getService(
+ context, 0, i, 0);
+
+ AlarmManager alarmManager = (AlarmManager)
+ context.getSystemService(Context.ALARM_SERVICE);
+
+ if (isOn) {
+ alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME,
+ SystemClock.elapsedRealtime(), POLL_INTERVAL_MS, pi);
+ } else {
+ alarmManager.cancel(pi);
+ pi.cancel();
+ }
+
+ QueryPreferences.setAlarmOn(context, isOn);
+ }
+
+ public static boolean isServiceAlarmOn(Context context) {
+ Intent i = PollService.newIntent(context);
+ PendingIntent pi = PendingIntent
+ .getService(context, 0, i, PendingIntent.FLAG_NO_CREATE);
+ return pi != null;
+ }
+
+ public PollService() {
+ super(TAG);
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+
+ if (!isNetworkAvailableAndConnected()) {
+ return;
+ }
+
+ String query = QueryPreferences.getStoredQuery(this);
+ String lastResultId = QueryPreferences.getLastResultId(this);
+ List items;
+
+ if (query == null) {
+ items = new FlickrFetchr().fetchRecentPhotos();
+ } else {
+ items = new FlickrFetchr().searchPhotos(query);
+ }
+
+ if (items.size() == 0) {
+ return;
+ }
+
+ String resultId = items.get(0).getId();
+ if (resultId.equals(lastResultId)) {
+ Log.i(TAG, "Got an old result: " + resultId);
+ } else {
+ Log.i(TAG, "Got a new result: " + resultId);
+
+ Resources resources = getResources();
+ Intent i = PhotoGalleryActivity.newIntent(this);
+ PendingIntent pi = PendingIntent
+ .getActivity(this, 0, i, 0);
+
+ Notification notification = new NotificationCompat.Builder(this)
+ .setTicker(resources.getString(R.string.new_pictures_title))
+ .setSmallIcon(android.R.drawable.ic_menu_report_image)
+ .setContentTitle(resources.getString(R.string.new_pictures_title))
+ .setContentText(resources.getString(R.string.new_pictures_text))
+ .setContentIntent(pi)
+ .setAutoCancel(true)
+ .build();
+
+ showBackgroundNotification(0, notification);
+ }
+
+ QueryPreferences.setLastResultId(this, resultId);
+ }
+
+ private void showBackgroundNotification(int requestCode, Notification notification) {
+ Intent i = new Intent(ACTION_SHOW_NOTIFICATION);
+ i.putExtra(REQUEST_CODE, requestCode);
+ i.putExtra(NOTIFICATION, notification);
+ sendOrderedBroadcast(i, PERM_PRIVATE, null, null,
+ Activity.RESULT_OK, null, null);
+ }
+
+ private boolean isNetworkAvailableAndConnected() {
+ ConnectivityManager cm =
+ (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
+
+ boolean isNetworkAvailable = cm.getActiveNetworkInfo() != null;
+ boolean isNetworkConnected = isNetworkAvailable &&
+ cm.getActiveNetworkInfo().isConnected();
+
+ return isNetworkConnected;
+ }
+}
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/QueryPreferences.java b/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/QueryPreferences.java
new file mode 100644
index 0000000..1b193a8
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/QueryPreferences.java
@@ -0,0 +1,48 @@
+package com.bignerdranch.android.photogallery;
+
+import android.content.Context;
+import android.preference.PreferenceManager;
+
+public class QueryPreferences {
+
+ private static final String PREF_SEARCH_QUERY = "searchQuery";
+ private static final String PREF_LAST_RESULT_ID = "lastResultId";
+ private static final String PREF_IS_ALARM_ON = "isAlarmOn";
+
+ public static String getStoredQuery(Context context) {
+ return PreferenceManager.getDefaultSharedPreferences(context)
+ .getString(PREF_SEARCH_QUERY, null);
+ }
+
+ public static void setStoredQuery(Context context, String query) {
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .putString(PREF_SEARCH_QUERY, query)
+ .apply();
+ }
+
+ public static String getLastResultId(Context context) {
+ return PreferenceManager.getDefaultSharedPreferences(context)
+ .getString(PREF_LAST_RESULT_ID, null);
+ }
+
+ public static void setLastResultId(Context context, String lastResultId) {
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .putString(PREF_LAST_RESULT_ID, lastResultId)
+ .apply();
+ }
+
+ public static boolean isAlarmOn(Context context) {
+ return PreferenceManager.getDefaultSharedPreferences(context)
+ .getBoolean(PREF_IS_ALARM_ON, false);
+ }
+
+ public static void setAlarmOn(Context context, boolean isOn) {
+ PreferenceManager.getDefaultSharedPreferences(context)
+ .edit()
+ .putBoolean(PREF_IS_ALARM_ON, isOn)
+ .apply();
+ }
+
+}
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/SingleFragmentActivity.java b/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/SingleFragmentActivity.java
new file mode 100644
index 0000000..faa98af
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/SingleFragmentActivity.java
@@ -0,0 +1,33 @@
+package com.bignerdranch.android.photogallery;
+
+import android.os.Bundle;
+import android.support.annotation.LayoutRes;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.AppCompatActivity;
+
+public abstract class SingleFragmentActivity extends AppCompatActivity {
+
+ protected abstract Fragment createFragment();
+
+ @LayoutRes
+ protected int getLayoutResId() {
+ return R.layout.activity_fragment;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(getLayoutResId());
+
+ FragmentManager fm = getSupportFragmentManager();
+ Fragment fragment = fm.findFragmentById(R.id.fragment_container);
+
+ if (fragment == null) {
+ fragment = createFragment();
+ fm.beginTransaction()
+ .add(R.id.fragment_container, fragment)
+ .commit();
+ }
+ }
+}
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/StartupReceiver.java b/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/StartupReceiver.java
new file mode 100644
index 0000000..804f076
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/StartupReceiver.java
@@ -0,0 +1,19 @@
+package com.bignerdranch.android.photogallery;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+public class StartupReceiver extends BroadcastReceiver{
+ private static final String TAG = "StartupReceiver";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Log.i(TAG, "Received broadcast intent: " + intent.getAction());
+
+ boolean isOn = QueryPreferences.isAlarmOn(context);
+ PollService.setServiceAlarm(context, isOn);
+ }
+
+}
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/ThumbnailDownloader.java b/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/ThumbnailDownloader.java
new file mode 100644
index 0000000..d3fc81b
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/ThumbnailDownloader.java
@@ -0,0 +1,102 @@
+package com.bignerdranch.android.photogallery;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+public class ThumbnailDownloader extends HandlerThread {
+ private static final String TAG = "ThumbnailDownloader";
+ private static final int MESSAGE_DOWNLOAD = 0;
+
+ private boolean mHasQuit = false;
+ private Handler mRequestHandler;
+ private ConcurrentMap mRequestMap = new ConcurrentHashMap<>();
+ private Handler mResponseHandler;
+ private ThumbnailDownloadListener mThumbnailDownloadListener;
+
+ public interface ThumbnailDownloadListener {
+ void onThumbnailDownloaded(T target, Bitmap bitmap);
+ }
+
+ public void setThumbnailDownloadListener(ThumbnailDownloadListener listener) {
+ mThumbnailDownloadListener = listener;
+ }
+
+ public ThumbnailDownloader(Handler responseHandler) {
+ super(TAG);
+ mResponseHandler = responseHandler;
+ }
+
+ @Override
+ protected void onLooperPrepared() {
+ mRequestHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MESSAGE_DOWNLOAD) {
+ T target = (T) msg.obj;
+ Log.i(TAG, "Got a request for URL: " + mRequestMap.get(target));
+ handleRequest(target);
+ }
+ }
+ };
+ }
+
+ @Override
+ public boolean quit() {
+ mHasQuit = true;
+ return super.quit();
+ }
+
+ public void queueThumbnail(T target, String url) {
+ Log.i(TAG, "Got a URL: " + url);
+
+ if (url == null) {
+ mRequestMap.remove(target);
+ } else {
+ mRequestMap.put(target, url);
+ mRequestHandler.obtainMessage(MESSAGE_DOWNLOAD, target)
+ .sendToTarget();
+ }
+ }
+
+ public void clearQueue() {
+ mRequestHandler.removeMessages(MESSAGE_DOWNLOAD);
+ mRequestMap.clear();
+ }
+
+ private void handleRequest(final T target) {
+ try {
+ final String url = mRequestMap.get(target);
+
+ if (url == null) {
+ return;
+ }
+
+ byte[] bitmapBytes = new FlickrFetchr().getUrlBytes(url);
+ final Bitmap bitmap = BitmapFactory
+ .decodeByteArray(bitmapBytes, 0, bitmapBytes.length);
+ Log.i(TAG, "Bitmap created");
+
+ mResponseHandler.post(new Runnable() {
+ public void run() {
+ if (mRequestMap.get(target) != url ||
+ mHasQuit) {
+ return;
+ }
+
+ mRequestMap.remove(target);
+ mThumbnailDownloadListener.onThumbnailDownloaded(target, bitmap);
+ }
+ });
+ } catch (IOException ioe) {
+ Log.e(TAG, "Error downloading image", ioe);
+ }
+ }
+}
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/VisibleFragment.java b/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/VisibleFragment.java
new file mode 100644
index 0000000..fc0a379
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/app/src/main/java/com/bignerdranch/android/photogallery/VisibleFragment.java
@@ -0,0 +1,38 @@
+package com.bignerdranch.android.photogallery;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.support.v4.app.Fragment;
+import android.util.Log;
+import android.widget.Toast;
+
+public abstract class VisibleFragment extends Fragment {
+ private static final String TAG = "VisibleFragment";
+
+ private BroadcastReceiver mOnShowNotification = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // If we receive this, we're visible, so cancel
+ // the notification
+ Log.i(TAG, "canceling notification");
+ setResultCode(Activity.RESULT_CANCELED);
+ }
+ };
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ IntentFilter filter = new IntentFilter(PollService.ACTION_SHOW_NOTIFICATION);
+ getActivity().registerReceiver(mOnShowNotification, filter,
+ PollService.PERM_PRIVATE, null);
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ getActivity().unregisterReceiver(mOnShowNotification);
+ }
+}
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/res/drawable/bill_up_close.png b/Book Files/30_WebView/PhotoGallery/app/src/main/res/drawable/bill_up_close.png
new file mode 100644
index 0000000..1e85792
Binary files /dev/null and b/Book Files/30_WebView/PhotoGallery/app/src/main/res/drawable/bill_up_close.png differ
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/res/layout/activity_fragment.xml b/Book Files/30_WebView/PhotoGallery/app/src/main/res/layout/activity_fragment.xml
new file mode 100644
index 0000000..1773143
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/app/src/main/res/layout/activity_fragment.xml
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/res/layout/fragment_photo_gallery.xml b/Book Files/30_WebView/PhotoGallery/app/src/main/res/layout/fragment_photo_gallery.xml
new file mode 100644
index 0000000..4af4094
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/app/src/main/res/layout/fragment_photo_gallery.xml
@@ -0,0 +1,8 @@
+
+
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/res/layout/fragment_photo_page.xml b/Book Files/30_WebView/PhotoGallery/app/src/main/res/layout/fragment_photo_page.xml
new file mode 100644
index 0000000..9e97d91
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/app/src/main/res/layout/fragment_photo_page.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/res/layout/list_item_gallery.xml b/Book Files/30_WebView/PhotoGallery/app/src/main/res/layout/list_item_gallery.xml
new file mode 100644
index 0000000..176311a
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/app/src/main/res/layout/list_item_gallery.xml
@@ -0,0 +1,7 @@
+
+
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/res/menu/fragment_photo_gallery.xml b/Book Files/30_WebView/PhotoGallery/app/src/main/res/menu/fragment_photo_gallery.xml
new file mode 100644
index 0000000..c31ba83
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/app/src/main/res/menu/fragment_photo_gallery.xml
@@ -0,0 +1,17 @@
+
+
\ No newline at end of file
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Book Files/30_WebView/PhotoGallery/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/Book Files/30_WebView/PhotoGallery/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Book Files/30_WebView/PhotoGallery/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/Book Files/30_WebView/PhotoGallery/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Book Files/30_WebView/PhotoGallery/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/Book Files/30_WebView/PhotoGallery/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Book Files/30_WebView/PhotoGallery/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/Book Files/30_WebView/PhotoGallery/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Book Files/30_WebView/PhotoGallery/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/Book Files/30_WebView/PhotoGallery/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/res/values-w820dp/dimens.xml b/Book Files/30_WebView/PhotoGallery/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/res/values/colors.xml b/Book Files/30_WebView/PhotoGallery/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/res/values/dimens.xml b/Book Files/30_WebView/PhotoGallery/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/res/values/strings.xml b/Book Files/30_WebView/PhotoGallery/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..f087f6d
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/app/src/main/res/values/strings.xml
@@ -0,0 +1,9 @@
+
+ PhotoGallery
+ Search
+ Clear Search
+ Start polling
+ Stop polling
+ New PhotoGallery Pictures
+ You have new pictures in PhotoGallery.
+
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/main/res/values/styles.xml b/Book Files/30_WebView/PhotoGallery/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Book Files/30_WebView/PhotoGallery/app/src/test/java/com/bignerdranch/android/photogallery/ExampleUnitTest.java b/Book Files/30_WebView/PhotoGallery/app/src/test/java/com/bignerdranch/android/photogallery/ExampleUnitTest.java
new file mode 100644
index 0000000..95126a2
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/app/src/test/java/com/bignerdranch/android/photogallery/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.photogallery;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/30_WebView/PhotoGallery/build.gradle b/Book Files/30_WebView/PhotoGallery/build.gradle
new file mode 100644
index 0000000..1ea4bd0
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Book Files/30_WebView/PhotoGallery/gradle.properties b/Book Files/30_WebView/PhotoGallery/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/Book Files/30_WebView/PhotoGallery/gradle/wrapper/gradle-wrapper.jar b/Book Files/30_WebView/PhotoGallery/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/Book Files/30_WebView/PhotoGallery/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/30_WebView/PhotoGallery/gradle/wrapper/gradle-wrapper.properties b/Book Files/30_WebView/PhotoGallery/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a82392d
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/30_WebView/PhotoGallery/gradlew b/Book Files/30_WebView/PhotoGallery/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/30_WebView/PhotoGallery/gradlew.bat b/Book Files/30_WebView/PhotoGallery/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/30_WebView/PhotoGallery/settings.gradle b/Book Files/30_WebView/PhotoGallery/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/30_WebView/PhotoGallery/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/31_TouchEvents/.DS_Store b/Book Files/31_TouchEvents/.DS_Store
new file mode 100644
index 0000000..5008ddf
Binary files /dev/null and b/Book Files/31_TouchEvents/.DS_Store differ
diff --git a/Book Files/31_TouchEvents/DragAndDraw/.gitignore b/Book Files/31_TouchEvents/DragAndDraw/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Book Files/31_TouchEvents/DragAndDraw/.idea/compiler.xml b/Book Files/31_TouchEvents/DragAndDraw/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/31_TouchEvents/DragAndDraw/.idea/copyright/profiles_settings.xml b/Book Files/31_TouchEvents/DragAndDraw/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Book Files/31_TouchEvents/DragAndDraw/.idea/encodings.xml b/Book Files/31_TouchEvents/DragAndDraw/.idea/encodings.xml
new file mode 100644
index 0000000..97626ba
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/31_TouchEvents/DragAndDraw/.idea/gradle.xml b/Book Files/31_TouchEvents/DragAndDraw/.idea/gradle.xml
new file mode 100644
index 0000000..4819d5a
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/.idea/gradle.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/31_TouchEvents/DragAndDraw/.idea/misc.xml b/Book Files/31_TouchEvents/DragAndDraw/.idea/misc.xml
new file mode 100644
index 0000000..5d19981
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/.idea/misc.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/31_TouchEvents/DragAndDraw/.idea/modules.xml b/Book Files/31_TouchEvents/DragAndDraw/.idea/modules.xml
new file mode 100644
index 0000000..c8fec22
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/31_TouchEvents/DragAndDraw/.idea/runConfigurations.xml b/Book Files/31_TouchEvents/DragAndDraw/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/31_TouchEvents/DragAndDraw/app/.gitignore b/Book Files/31_TouchEvents/DragAndDraw/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/31_TouchEvents/DragAndDraw/app/build.gradle b/Book Files/31_TouchEvents/DragAndDraw/app/build.gradle
new file mode 100644
index 0000000..340d1ff
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/app/build.gradle
@@ -0,0 +1,30 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ applicationId "com.bignerdranch.android.draganddraw"
+ minSdkVersion 19
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ compile 'com.android.support.constraint:constraint-layout:1.0.2'
+ testCompile 'junit:junit:4.12'
+}
diff --git a/Book Files/31_TouchEvents/DragAndDraw/app/proguard-rules.pro b/Book Files/31_TouchEvents/DragAndDraw/app/proguard-rules.pro
new file mode 100644
index 0000000..82464fa
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/app/proguard-rules.pro
@@ -0,0 +1,25 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/chris/AndroidDeveloper/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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/Book Files/31_TouchEvents/DragAndDraw/app/src/androidTest/java/com/bignerdranch/android/draganddraw/ExampleInstrumentedTest.java b/Book Files/31_TouchEvents/DragAndDraw/app/src/androidTest/java/com/bignerdranch/android/draganddraw/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..37a299d
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/app/src/androidTest/java/com/bignerdranch/android/draganddraw/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.draganddraw;
+
+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.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.bignerdranch.android.draganddraw", appContext.getPackageName());
+ }
+}
diff --git a/Book Files/31_TouchEvents/DragAndDraw/app/src/main/AndroidManifest.xml b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..d285ec9
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/AndroidManifest.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/31_TouchEvents/DragAndDraw/app/src/main/java/com/bignerdranch/android/draganddraw/Box.java b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/java/com/bignerdranch/android/draganddraw/Box.java
new file mode 100644
index 0000000..590c745
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/java/com/bignerdranch/android/draganddraw/Box.java
@@ -0,0 +1,25 @@
+package com.bignerdranch.android.draganddraw;
+
+import android.graphics.PointF;
+
+public class Box {
+ private PointF mOrigin;
+ private PointF mCurrent;
+
+ public Box(PointF origin) {
+ mOrigin = origin;
+ mCurrent = origin;
+ }
+
+ public PointF getCurrent() {
+ return mCurrent;
+ }
+
+ public void setCurrent(PointF current) {
+ mCurrent = current;
+ }
+
+ public PointF getOrigin() {
+ return mOrigin;
+ }
+}
diff --git a/Book Files/31_TouchEvents/DragAndDraw/app/src/main/java/com/bignerdranch/android/draganddraw/BoxDrawingView.java b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/java/com/bignerdranch/android/draganddraw/BoxDrawingView.java
new file mode 100644
index 0000000..9fcbd73
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/java/com/bignerdranch/android/draganddraw/BoxDrawingView.java
@@ -0,0 +1,89 @@
+package com.bignerdranch.android.draganddraw;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PointF;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class BoxDrawingView extends View {
+ private static final String TAG = "BoxDrawingView";
+
+ private Box mCurrentBox;
+ private List mBoxen = new ArrayList<>();
+ private Paint mBoxPaint;
+ private Paint mBackgroundPaint;
+
+ // Used when creating the view in code
+ public BoxDrawingView(Context context) {
+ this(context, null);
+ }
+
+ // Used when inflating the view from XML
+ public BoxDrawingView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ // Paint the boxes a nice semitransparent red (ARGB)
+ mBoxPaint = new Paint();
+ mBoxPaint.setColor(0x22ff0000);
+
+ // Paint the background off-white
+ mBackgroundPaint = new Paint();
+ mBackgroundPaint.setColor(0xfff8efe0);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ // Fill the background
+ canvas.drawPaint(mBackgroundPaint);
+
+ for (Box box : mBoxen) {
+ float left = Math.min(box.getOrigin().x, box.getCurrent().x);
+ float right = Math.max(box.getOrigin().x, box.getCurrent().x);
+ float top = Math.min(box.getOrigin().y, box.getCurrent().y);
+ float bottom = Math.max(box.getOrigin().y, box.getCurrent().y);
+
+ canvas.drawRect(left, top, right, bottom, mBoxPaint);
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ PointF current = new PointF(event.getX(), event.getY());
+ String action = "";
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ action = "ACTION_DOWN";
+ // Reset drawing state
+ mCurrentBox = new Box(current);
+ mBoxen.add(mCurrentBox);
+ break;
+ case MotionEvent.ACTION_MOVE:
+ action = "ACTION_MOVE";
+ if (mCurrentBox != null) {
+ mCurrentBox.setCurrent(current);
+ invalidate();
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ action = "ACTION_UP";
+ mCurrentBox = null;
+ break;
+ case MotionEvent.ACTION_CANCEL:
+ action = "ACTION_CANCEL";
+ mCurrentBox = null;
+ break;
+ }
+
+ Log.i(TAG, action + " at x=" + current.x +
+ ", y=" + current.y);
+
+ return true;
+ }
+}
diff --git a/Book Files/31_TouchEvents/DragAndDraw/app/src/main/java/com/bignerdranch/android/draganddraw/DragAndDrawActivity.java b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/java/com/bignerdranch/android/draganddraw/DragAndDrawActivity.java
new file mode 100644
index 0000000..b639158
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/java/com/bignerdranch/android/draganddraw/DragAndDrawActivity.java
@@ -0,0 +1,11 @@
+package com.bignerdranch.android.draganddraw;
+
+import android.support.v4.app.Fragment;
+
+public class DragAndDrawActivity extends SingleFragmentActivity {
+
+ @Override
+ protected Fragment createFragment() {
+ return DragAndDrawFragment.newInstance();
+ }
+}
diff --git a/Book Files/31_TouchEvents/DragAndDraw/app/src/main/java/com/bignerdranch/android/draganddraw/DragAndDrawFragment.java b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/java/com/bignerdranch/android/draganddraw/DragAndDrawFragment.java
new file mode 100644
index 0000000..200561b
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/java/com/bignerdranch/android/draganddraw/DragAndDrawFragment.java
@@ -0,0 +1,21 @@
+package com.bignerdranch.android.draganddraw;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class DragAndDrawFragment extends Fragment {
+
+ public static DragAndDrawFragment newInstance() {
+ return new DragAndDrawFragment();
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.fragment_drag_and_draw, container, false);
+ return v;
+ }
+}
diff --git a/Book Files/31_TouchEvents/DragAndDraw/app/src/main/java/com/bignerdranch/android/draganddraw/SingleFragmentActivity.java b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/java/com/bignerdranch/android/draganddraw/SingleFragmentActivity.java
new file mode 100644
index 0000000..f50d2a7
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/java/com/bignerdranch/android/draganddraw/SingleFragmentActivity.java
@@ -0,0 +1,27 @@
+package com.bignerdranch.android.draganddraw;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.AppCompatActivity;
+
+public abstract class SingleFragmentActivity extends AppCompatActivity {
+
+ protected abstract Fragment createFragment();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_fragment);
+
+ FragmentManager fm = getSupportFragmentManager();
+ Fragment fragment = fm.findFragmentById(R.id.fragment_container);
+
+ if (fragment == null) {
+ fragment = createFragment();
+ fm.beginTransaction()
+ .add(R.id.fragment_container, fragment)
+ .commit();
+ }
+ }
+}
diff --git a/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/layout/activity_fragment.xml b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/layout/activity_fragment.xml
new file mode 100644
index 0000000..1773143
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/layout/activity_fragment.xml
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/layout/fragment_drag_and_draw.xml b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/layout/fragment_drag_and_draw.xml
new file mode 100644
index 0000000..105c649
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/layout/fragment_drag_and_draw.xml
@@ -0,0 +1,5 @@
+
+
\ No newline at end of file
diff --git a/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000..4c2d454
Binary files /dev/null and b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..56bfbe5
Binary files /dev/null and b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000..52d8318
Binary files /dev/null and b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..d19e914
Binary files /dev/null and b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000..3d70a95
Binary files /dev/null and b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..77c5d2f
Binary files /dev/null and b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..01dad87
Binary files /dev/null and b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..4aa0880
Binary files /dev/null and b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..4d7405f
Binary files /dev/null and b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..a0aebbc
Binary files /dev/null and b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/values/colors.xml b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/values/strings.xml b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..6da6388
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ DragAndDraw
+
diff --git a/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/values/styles.xml b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Book Files/31_TouchEvents/DragAndDraw/app/src/test/java/com/bignerdranch/android/draganddraw/ExampleUnitTest.java b/Book Files/31_TouchEvents/DragAndDraw/app/src/test/java/com/bignerdranch/android/draganddraw/ExampleUnitTest.java
new file mode 100644
index 0000000..dcfd591
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/app/src/test/java/com/bignerdranch/android/draganddraw/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.draganddraw;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/31_TouchEvents/DragAndDraw/build.gradle b/Book Files/31_TouchEvents/DragAndDraw/build.gradle
new file mode 100644
index 0000000..1ea4bd0
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Book Files/31_TouchEvents/DragAndDraw/gradle.properties b/Book Files/31_TouchEvents/DragAndDraw/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/Book Files/31_TouchEvents/DragAndDraw/gradle/wrapper/gradle-wrapper.jar b/Book Files/31_TouchEvents/DragAndDraw/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/Book Files/31_TouchEvents/DragAndDraw/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/31_TouchEvents/DragAndDraw/gradle/wrapper/gradle-wrapper.properties b/Book Files/31_TouchEvents/DragAndDraw/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a82392d
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/31_TouchEvents/DragAndDraw/gradlew b/Book Files/31_TouchEvents/DragAndDraw/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/31_TouchEvents/DragAndDraw/gradlew.bat b/Book Files/31_TouchEvents/DragAndDraw/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/31_TouchEvents/DragAndDraw/settings.gradle b/Book Files/31_TouchEvents/DragAndDraw/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/31_TouchEvents/DragAndDraw/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/31_TouchEvents/LICENSE.txt b/Book Files/31_TouchEvents/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Book Files/31_TouchEvents/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Book Files/32_Animations/.DS_Store b/Book Files/32_Animations/.DS_Store
new file mode 100644
index 0000000..5008ddf
Binary files /dev/null and b/Book Files/32_Animations/.DS_Store differ
diff --git a/Book Files/32_Animations/LICENSE.txt b/Book Files/32_Animations/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Book Files/32_Animations/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Book Files/32_Animations/Sunset/.gitignore b/Book Files/32_Animations/Sunset/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Book Files/32_Animations/Sunset/.idea/compiler.xml b/Book Files/32_Animations/Sunset/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/32_Animations/Sunset/.idea/copyright/profiles_settings.xml b/Book Files/32_Animations/Sunset/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Book Files/32_Animations/Sunset/.idea/encodings.xml b/Book Files/32_Animations/Sunset/.idea/encodings.xml
new file mode 100644
index 0000000..97626ba
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/32_Animations/Sunset/.idea/gradle.xml b/Book Files/32_Animations/Sunset/.idea/gradle.xml
new file mode 100644
index 0000000..7ac24c7
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/.idea/gradle.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/32_Animations/Sunset/.idea/misc.xml b/Book Files/32_Animations/Sunset/.idea/misc.xml
new file mode 100644
index 0000000..5d19981
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/.idea/misc.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/32_Animations/Sunset/.idea/modules.xml b/Book Files/32_Animations/Sunset/.idea/modules.xml
new file mode 100644
index 0000000..184a461
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/32_Animations/Sunset/.idea/runConfigurations.xml b/Book Files/32_Animations/Sunset/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/32_Animations/Sunset/app/.gitignore b/Book Files/32_Animations/Sunset/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/32_Animations/Sunset/app/build.gradle b/Book Files/32_Animations/Sunset/app/build.gradle
new file mode 100644
index 0000000..19802ff
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/app/build.gradle
@@ -0,0 +1,30 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ applicationId "com.bignerdranch.android.sunset"
+ minSdkVersion 19
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ compile 'com.android.support.constraint:constraint-layout:1.0.2'
+ testCompile 'junit:junit:4.12'
+}
diff --git a/Book Files/32_Animations/Sunset/app/proguard-rules.pro b/Book Files/32_Animations/Sunset/app/proguard-rules.pro
new file mode 100644
index 0000000..82464fa
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/app/proguard-rules.pro
@@ -0,0 +1,25 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/chris/AndroidDeveloper/sdk/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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/Book Files/32_Animations/Sunset/app/src/androidTest/java/com/bignerdranch/android/sunset/ExampleInstrumentedTest.java b/Book Files/32_Animations/Sunset/app/src/androidTest/java/com/bignerdranch/android/sunset/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..8726cef
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/app/src/androidTest/java/com/bignerdranch/android/sunset/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.sunset;
+
+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.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.bignerdranch.android.sunset", appContext.getPackageName());
+ }
+}
diff --git a/Book Files/32_Animations/Sunset/app/src/main/AndroidManifest.xml b/Book Files/32_Animations/Sunset/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..eef223a
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/app/src/main/AndroidManifest.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/32_Animations/Sunset/app/src/main/java/com/bignerdranch/android/sunset/SingleFragmentActivity.java b/Book Files/32_Animations/Sunset/app/src/main/java/com/bignerdranch/android/sunset/SingleFragmentActivity.java
new file mode 100644
index 0000000..9074b02
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/app/src/main/java/com/bignerdranch/android/sunset/SingleFragmentActivity.java
@@ -0,0 +1,27 @@
+package com.bignerdranch.android.sunset;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.AppCompatActivity;
+
+public abstract class SingleFragmentActivity extends AppCompatActivity {
+
+ protected abstract Fragment createFragment();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_fragment);
+
+ FragmentManager fm = getSupportFragmentManager();
+ Fragment fragment = fm.findFragmentById(R.id.fragment_container);
+
+ if (fragment == null) {
+ fragment = createFragment();
+ fm.beginTransaction()
+ .add(R.id.fragment_container, fragment)
+ .commit();
+ }
+ }
+}
diff --git a/Book Files/32_Animations/Sunset/app/src/main/java/com/bignerdranch/android/sunset/SunsetActivity.java b/Book Files/32_Animations/Sunset/app/src/main/java/com/bignerdranch/android/sunset/SunsetActivity.java
new file mode 100644
index 0000000..9b0bf88
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/app/src/main/java/com/bignerdranch/android/sunset/SunsetActivity.java
@@ -0,0 +1,11 @@
+package com.bignerdranch.android.sunset;
+
+import android.support.v4.app.Fragment;
+
+public class SunsetActivity extends SingleFragmentActivity {
+
+ @Override
+ protected Fragment createFragment() {
+ return SunsetFragment.newInstance();
+ }
+}
diff --git a/Book Files/32_Animations/Sunset/app/src/main/java/com/bignerdranch/android/sunset/SunsetFragment.java b/Book Files/32_Animations/Sunset/app/src/main/java/com/bignerdranch/android/sunset/SunsetFragment.java
new file mode 100644
index 0000000..eb2aa02
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/app/src/main/java/com/bignerdranch/android/sunset/SunsetFragment.java
@@ -0,0 +1,78 @@
+package com.bignerdranch.android.sunset;
+
+import android.animation.AnimatorSet;
+import android.animation.ArgbEvaluator;
+import android.animation.ObjectAnimator;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AccelerateInterpolator;
+
+public class SunsetFragment extends Fragment {
+
+ private View mSceneView;
+ private View mSunView;
+ private View mSkyView;
+
+ private int mBlueSkyColor;
+ private int mSunsetSkyColor;
+ private int mNightSkyColor;
+
+ public static SunsetFragment newInstance() {
+ return new SunsetFragment();
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_sunset, container, false);
+
+ mSceneView = view;
+ mSunView = view.findViewById(R.id.sun);
+ mSkyView = view.findViewById(R.id.sky);
+
+ Resources resources = getResources();
+ mBlueSkyColor = resources.getColor(R.color.blue_sky);
+ mSunsetSkyColor = resources.getColor(R.color.sunset_sky);
+ mNightSkyColor = resources.getColor(R.color.night_sky);
+
+ mSceneView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ startAnimation();
+ }
+ });
+
+ return view;
+ }
+
+ private void startAnimation() {
+ float sunYStart = mSunView.getTop();
+ float sunYEnd = mSkyView.getHeight();
+
+ ObjectAnimator heightAnimator = ObjectAnimator
+ .ofFloat(mSunView, "y", sunYStart, sunYEnd)
+ .setDuration(3000);
+ heightAnimator.setInterpolator(new AccelerateInterpolator());
+
+ ObjectAnimator sunsetSkyAnimator = ObjectAnimator
+ .ofInt(mSkyView, "backgroundColor", mBlueSkyColor, mSunsetSkyColor)
+ .setDuration(3000);
+ sunsetSkyAnimator.setEvaluator(new ArgbEvaluator());
+
+ ObjectAnimator nightSkyAnimator = ObjectAnimator
+ .ofInt(mSkyView, "backgroundColor", mSunsetSkyColor, mNightSkyColor)
+ .setDuration(1500);
+ nightSkyAnimator.setEvaluator(new ArgbEvaluator());
+
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet
+ .play(heightAnimator)
+ .with(sunsetSkyAnimator)
+ .before(nightSkyAnimator);
+ animatorSet.start();
+ }
+}
diff --git a/Book Files/32_Animations/Sunset/app/src/main/res/drawable/sun.xml b/Book Files/32_Animations/Sunset/app/src/main/res/drawable/sun.xml
new file mode 100644
index 0000000..58d98ea
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/app/src/main/res/drawable/sun.xml
@@ -0,0 +1,5 @@
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/32_Animations/Sunset/app/src/main/res/layout/activity_fragment.xml b/Book Files/32_Animations/Sunset/app/src/main/res/layout/activity_fragment.xml
new file mode 100644
index 0000000..1773143
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/app/src/main/res/layout/activity_fragment.xml
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/Book Files/32_Animations/Sunset/app/src/main/res/layout/activity_sunset.xml b/Book Files/32_Animations/Sunset/app/src/main/res/layout/activity_sunset.xml
new file mode 100644
index 0000000..8c713ba
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/app/src/main/res/layout/activity_sunset.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
diff --git a/Book Files/32_Animations/Sunset/app/src/main/res/layout/fragment_sunset.xml b/Book Files/32_Animations/Sunset/app/src/main/res/layout/fragment_sunset.xml
new file mode 100644
index 0000000..0c5c9ff
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/app/src/main/res/layout/fragment_sunset.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-hdpi/ic_launcher.webp
new file mode 100644
index 0000000..4c2d454
Binary files /dev/null and b/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ
diff --git a/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..56bfbe5
Binary files /dev/null and b/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ
diff --git a/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-mdpi/ic_launcher.webp
new file mode 100644
index 0000000..52d8318
Binary files /dev/null and b/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ
diff --git a/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..d19e914
Binary files /dev/null and b/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ
diff --git a/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
new file mode 100644
index 0000000..3d70a95
Binary files /dev/null and b/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ
diff --git a/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..77c5d2f
Binary files /dev/null and b/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ
diff --git a/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..01dad87
Binary files /dev/null and b/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ
diff --git a/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..4aa0880
Binary files /dev/null and b/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ
diff --git a/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
new file mode 100644
index 0000000..4d7405f
Binary files /dev/null and b/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ
diff --git a/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
new file mode 100644
index 0000000..a0aebbc
Binary files /dev/null and b/Book Files/32_Animations/Sunset/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ
diff --git a/Book Files/32_Animations/Sunset/app/src/main/res/values/colors.xml b/Book Files/32_Animations/Sunset/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..ea3a26e
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/app/src/main/res/values/colors.xml
@@ -0,0 +1,12 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
+ #fcfcb7
+ #1e7ac7
+ #ec8100
+ #05192e
+ #224869
+
diff --git a/Book Files/32_Animations/Sunset/app/src/main/res/values/strings.xml b/Book Files/32_Animations/Sunset/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..7976a60
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Sunset
+
diff --git a/Book Files/32_Animations/Sunset/app/src/main/res/values/styles.xml b/Book Files/32_Animations/Sunset/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Book Files/32_Animations/Sunset/app/src/test/java/com/bignerdranch/android/sunset/ExampleUnitTest.java b/Book Files/32_Animations/Sunset/app/src/test/java/com/bignerdranch/android/sunset/ExampleUnitTest.java
new file mode 100644
index 0000000..a3c5580
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/app/src/test/java/com/bignerdranch/android/sunset/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.sunset;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/32_Animations/Sunset/build.gradle b/Book Files/32_Animations/Sunset/build.gradle
new file mode 100644
index 0000000..1ea4bd0
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Book Files/32_Animations/Sunset/gradle.properties b/Book Files/32_Animations/Sunset/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/Book Files/32_Animations/Sunset/gradle/wrapper/gradle-wrapper.jar b/Book Files/32_Animations/Sunset/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/Book Files/32_Animations/Sunset/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/32_Animations/Sunset/gradle/wrapper/gradle-wrapper.properties b/Book Files/32_Animations/Sunset/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a82392d
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/32_Animations/Sunset/gradlew b/Book Files/32_Animations/Sunset/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/32_Animations/Sunset/gradlew.bat b/Book Files/32_Animations/Sunset/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/32_Animations/Sunset/settings.gradle b/Book Files/32_Animations/Sunset/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/32_Animations/Sunset/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/33_Locations/.DS_Store b/Book Files/33_Locations/.DS_Store
new file mode 100755
index 0000000..5350bcf
Binary files /dev/null and b/Book Files/33_Locations/.DS_Store differ
diff --git a/Book Files/33_Locations/LICENSE.txt b/Book Files/33_Locations/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Book Files/33_Locations/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Book Files/33_Locations/Locatr/.gitignore b/Book Files/33_Locations/Locatr/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Book Files/33_Locations/Locatr/.idea/compiler.xml b/Book Files/33_Locations/Locatr/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/33_Locations/Locatr/.idea/copyright/profiles_settings.xml b/Book Files/33_Locations/Locatr/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Book Files/33_Locations/Locatr/.idea/encodings.xml b/Book Files/33_Locations/Locatr/.idea/encodings.xml
new file mode 100644
index 0000000..97626ba
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/33_Locations/Locatr/.idea/gradle.xml b/Book Files/33_Locations/Locatr/.idea/gradle.xml
new file mode 100644
index 0000000..7ac24c7
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/.idea/gradle.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/33_Locations/Locatr/.idea/misc.xml b/Book Files/33_Locations/Locatr/.idea/misc.xml
new file mode 100644
index 0000000..5d19981
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/.idea/misc.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/33_Locations/Locatr/.idea/modules.xml b/Book Files/33_Locations/Locatr/.idea/modules.xml
new file mode 100644
index 0000000..7fc7a24
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/33_Locations/Locatr/.idea/runConfigurations.xml b/Book Files/33_Locations/Locatr/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/33_Locations/Locatr/app/.gitignore b/Book Files/33_Locations/Locatr/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/33_Locations/Locatr/app/build.gradle b/Book Files/33_Locations/Locatr/app/build.gradle
new file mode 100644
index 0000000..9cd7219
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/app/build.gradle
@@ -0,0 +1,30 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ applicationId "com.bignerdranch.android.locatr"
+ minSdkVersion 19
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ testCompile 'junit:junit:4.12'
+ compile 'com.google.android.gms:play-services-location:9.8.0'
+}
diff --git a/Book Files/33_Locations/Locatr/app/proguard-rules.pro b/Book Files/33_Locations/Locatr/app/proguard-rules.pro
new file mode 100644
index 0000000..cf5610c
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/bphillips/devtools/android-sdk-macosx/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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 *;
+#}
diff --git a/Book Files/33_Locations/Locatr/app/src/androidTest/java/com/bignerdranch/android/locatr/ExampleInstrumentedTest.java b/Book Files/33_Locations/Locatr/app/src/androidTest/java/com/bignerdranch/android/locatr/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..82fb339
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/app/src/androidTest/java/com/bignerdranch/android/locatr/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.locatr;
+
+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.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.bignerdranch.android.locatr", appContext.getPackageName());
+ }
+}
diff --git a/Book Files/33_Locations/Locatr/app/src/main/AndroidManifest.xml b/Book Files/33_Locations/Locatr/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..d102d72
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/app/src/main/AndroidManifest.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/33_Locations/Locatr/app/src/main/java/com/bignerdranch/android/locatr/FlickrFetchr.java b/Book Files/33_Locations/Locatr/app/src/main/java/com/bignerdranch/android/locatr/FlickrFetchr.java
new file mode 100644
index 0000000..6af9064
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/app/src/main/java/com/bignerdranch/android/locatr/FlickrFetchr.java
@@ -0,0 +1,135 @@
+package com.bignerdranch.android.locatr;
+
+import android.location.Location;
+import android.net.Uri;
+import android.util.Log;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+public class FlickrFetchr {
+ private static final String TAG = "FlickrFetchr";
+
+ private static final String API_KEY = "REPLACE_ME_WITH_A_REAL_KEY";
+ private static final String FETCH_RECENTS_METHOD = "flickr.photos.getRecent";
+ private static final String SEARCH_METHOD = "flickr.photos.search";
+ private static final Uri ENDPOINT = Uri
+ .parse("https://api.flickr.com/services/rest/")
+ .buildUpon()
+ .appendQueryParameter("api_key", API_KEY)
+ .appendQueryParameter("format", "json")
+ .appendQueryParameter("nojsoncallback", "1")
+ .appendQueryParameter("extras", "url_s")
+ .build();
+
+ public byte[] getUrlBytes(String urlSpec) throws IOException {
+ URL url = new URL(urlSpec);
+ HttpURLConnection connection = (HttpURLConnection)url.openConnection();
+ try {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ InputStream in = connection.getInputStream();
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ throw new IOException(connection.getResponseMessage() +
+ ": with " +
+ urlSpec);
+ }
+ int bytesRead = 0;
+ byte[] buffer = new byte[1024];
+ while ((bytesRead = in.read(buffer)) > 0) {
+ out.write(buffer, 0, bytesRead);
+ }
+ out.close();
+ return out.toByteArray();
+ } finally {
+ connection.disconnect();
+ }
+ }
+
+ public String getUrlString(String urlSpec) throws IOException {
+ return new String(getUrlBytes(urlSpec));
+ }
+
+ public List fetchRecentPhotos() {
+ String url = buildUrl(FETCH_RECENTS_METHOD, null);
+ return downloadGalleryItems(url);
+ }
+
+ public List searchPhotos(String query) {
+ String url = buildUrl(SEARCH_METHOD, query);
+ return downloadGalleryItems(url);
+ }
+
+ public List searchPhotos(Location location) {
+ String url = buildUrl(location);
+ return downloadGalleryItems(url);
+ }
+
+ private List downloadGalleryItems(String url) {
+ List items = new ArrayList<>();
+
+ try {
+ String jsonString = getUrlString(url);
+ Log.i(TAG, "Received JSON: " + jsonString);
+ JSONObject jsonBody = new JSONObject(jsonString);
+ parseItems(items, jsonBody);
+ } catch (IOException ioe) {
+ Log.e(TAG, "Failed to fetch items", ioe);
+ } catch (JSONException je) {
+ Log.e(TAG, "Failed to parse JSON", je);
+ }
+
+ return items;
+ }
+
+ private String buildUrl(String method, String query) {
+ Uri.Builder uriBuilder = ENDPOINT.buildUpon()
+ .appendQueryParameter("method", method);
+
+ if (method.equals(SEARCH_METHOD)) {
+ uriBuilder.appendQueryParameter("text", query);
+ }
+
+ return uriBuilder.build().toString();
+ }
+
+ private String buildUrl(Location location) {
+ return ENDPOINT.buildUpon()
+ .appendQueryParameter("method", SEARCH_METHOD)
+ .appendQueryParameter("lat", "" + location.getLatitude())
+ .appendQueryParameter("lon", "" + location.getLongitude())
+ .build().toString();
+ }
+
+ private void parseItems(List items, JSONObject jsonBody)
+ throws IOException, JSONException {
+
+ JSONObject photosJsonObject = jsonBody.getJSONObject("photos");
+ JSONArray photoJsonArray = photosJsonObject.getJSONArray("photo");
+
+ for (int i = 0; i < photoJsonArray.length(); i++) {
+ JSONObject photoJsonObject = photoJsonArray.getJSONObject(i);
+
+ GalleryItem item = new GalleryItem();
+ item.setId(photoJsonObject.getString("id"));
+ item.setCaption(photoJsonObject.getString("title"));
+
+ if (!photoJsonObject.has("url_s")) {
+ continue;
+ }
+
+ item.setUrl(photoJsonObject.getString("url_s"));
+ item.setOwner(photoJsonObject.getString("owner"));
+ items.add(item);
+ }
+ }
+
+}
diff --git a/Book Files/33_Locations/Locatr/app/src/main/java/com/bignerdranch/android/locatr/GalleryItem.java b/Book Files/33_Locations/Locatr/app/src/main/java/com/bignerdranch/android/locatr/GalleryItem.java
new file mode 100644
index 0000000..124d0ed
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/app/src/main/java/com/bignerdranch/android/locatr/GalleryItem.java
@@ -0,0 +1,55 @@
+package com.bignerdranch.android.locatr;
+
+import android.net.Uri;
+
+public class GalleryItem {
+ private String mCaption;
+ private String mId;
+ private String mUrl;
+ private String mOwner;
+
+ public String getCaption() {
+ return mCaption;
+ }
+
+ public void setCaption(String caption) {
+ mCaption = caption;
+ }
+
+ public String getId() {
+ return mId;
+ }
+
+ public void setId(String id) {
+ mId = id;
+ }
+
+ public String getUrl() {
+ return mUrl;
+ }
+
+ public void setUrl(String url) {
+ mUrl = url;
+ }
+
+ public String getOwner() {
+ return mOwner;
+ }
+
+ public void setOwner(String owner) {
+ mOwner = owner;
+ }
+
+ public Uri getPhotoPageUri() {
+ return Uri.parse("http://www.flickr.com/photos/")
+ .buildUpon()
+ .appendPath(mOwner)
+ .appendPath(mId)
+ .build();
+ }
+
+ @Override
+ public String toString() {
+ return mCaption;
+ }
+}
diff --git a/Book Files/33_Locations/Locatr/app/src/main/java/com/bignerdranch/android/locatr/LocatrActivity.java b/Book Files/33_Locations/Locatr/app/src/main/java/com/bignerdranch/android/locatr/LocatrActivity.java
new file mode 100644
index 0000000..1dc8d0a
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/app/src/main/java/com/bignerdranch/android/locatr/LocatrActivity.java
@@ -0,0 +1,42 @@
+package com.bignerdranch.android.locatr;
+
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.support.v4.app.Fragment;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.GoogleApiAvailability;
+
+public class LocatrActivity extends SingleFragmentActivity {
+ private static final int REQUEST_ERROR = 0;
+
+ @Override
+ protected Fragment createFragment() {
+ return LocatrFragment.newInstance();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance();
+ int errorCode = apiAvailability.isGooglePlayServicesAvailable(this);
+
+ if (errorCode != ConnectionResult.SUCCESS) {
+ Dialog errorDialog = apiAvailability.getErrorDialog(this,
+ errorCode,
+ REQUEST_ERROR,
+ new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialogInterface) {
+ // Leave if services are unavailable.
+ finish();
+ }
+ });
+
+ errorDialog.show();
+ }
+ }
+}
diff --git a/Book Files/33_Locations/Locatr/app/src/main/java/com/bignerdranch/android/locatr/LocatrFragment.java b/Book Files/33_Locations/Locatr/app/src/main/java/com/bignerdranch/android/locatr/LocatrFragment.java
new file mode 100644
index 0000000..91e25e4
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/app/src/main/java/com/bignerdranch/android/locatr/LocatrFragment.java
@@ -0,0 +1,178 @@
+package com.bignerdranch.android.locatr;
+
+import android.Manifest;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.location.Location;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v4.content.ContextCompat;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.location.LocationListener;
+import com.google.android.gms.location.LocationRequest;
+import com.google.android.gms.location.LocationServices;
+
+import java.io.IOException;
+import java.util.List;
+
+
+public class LocatrFragment extends Fragment {
+ private static final String TAG = "LocatrFragment";
+ private static final String[] LOCATION_PERMISSIONS = new String[]{
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.ACCESS_COARSE_LOCATION,
+ };
+ private static final int REQUEST_LOCATION_PERMISSIONS = 0;
+
+ private ImageView mImageView;
+ private GoogleApiClient mClient;
+
+ public static LocatrFragment newInstance() {
+ return new LocatrFragment();
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+
+ mClient = new GoogleApiClient.Builder(getActivity()).addApi(LocationServices.API)
+ .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
+ @Override
+ public void onConnected(@Nullable Bundle bundle) {
+ getActivity().invalidateOptionsMenu();
+ }
+
+ @Override
+ public void onConnectionSuspended(int i) {
+
+ }
+ })
+ .build();
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.fragment_locatr, container, false);
+
+ mImageView = (ImageView) v.findViewById(R.id.image);
+ return v;
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ getActivity().invalidateOptionsMenu();
+ mClient.connect();
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+
+ mClient.disconnect();
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ inflater.inflate(R.menu.fragment_locatr, menu);
+
+ MenuItem searchItem = menu.findItem(R.id.action_locate);
+ searchItem.setEnabled(mClient.isConnected());
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.action_locate:
+ if (hasLocationPermission()) {
+ findImage();
+ } else {
+ requestPermissions(LOCATION_PERMISSIONS,
+ REQUEST_LOCATION_PERMISSIONS);
+ }
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String[] permissions,
+ int[] grantResults) {
+ switch (requestCode) {
+ case REQUEST_LOCATION_PERMISSIONS:
+ if (hasLocationPermission()) {
+ findImage();
+ }
+ default:
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ }
+ }
+
+ private void findImage() {
+ LocationRequest request = LocationRequest.create();
+ request.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
+ request.setNumUpdates(1);
+ request.setInterval(0);
+ LocationServices.FusedLocationApi
+ .requestLocationUpdates(mClient, request, new LocationListener() {
+ @Override
+ public void onLocationChanged(Location location) {
+ Log.i(TAG, "Got a fix: " + location);
+ new SearchTask().execute(location);
+ }
+ });
+ }
+
+ private boolean hasLocationPermission() {
+ int result = ContextCompat
+ .checkSelfPermission(getActivity(), LOCATION_PERMISSIONS[0]);
+ return result == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private class SearchTask extends AsyncTask {
+ private GalleryItem mGalleryItem;
+ private Bitmap mBitmap;
+
+ @Override
+ protected Void doInBackground(Location... params) {
+ FlickrFetchr fetchr = new FlickrFetchr();
+ List items = fetchr.searchPhotos(params[0]);
+
+ if (items.size() == 0) {
+ return null;
+ }
+
+ mGalleryItem = items.get(0);
+
+ try {
+ byte[] bytes = fetchr.getUrlBytes(mGalleryItem.getUrl());
+ mBitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
+ } catch (IOException ioe) {
+ Log.i(TAG, "Unable to decode bitmap", ioe);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ mImageView.setImageBitmap(mBitmap);
+ }
+ }
+}
diff --git a/Book Files/33_Locations/Locatr/app/src/main/java/com/bignerdranch/android/locatr/SingleFragmentActivity.java b/Book Files/33_Locations/Locatr/app/src/main/java/com/bignerdranch/android/locatr/SingleFragmentActivity.java
new file mode 100644
index 0000000..5318df8
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/app/src/main/java/com/bignerdranch/android/locatr/SingleFragmentActivity.java
@@ -0,0 +1,28 @@
+package com.bignerdranch.android.locatr;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.AppCompatActivity;
+
+public abstract class SingleFragmentActivity extends AppCompatActivity {
+
+ protected abstract Fragment createFragment();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_fragment);
+
+ FragmentManager fm = getSupportFragmentManager();
+ Fragment fragment = fm.findFragmentById(R.id.fragment_container);
+
+ if (fragment == null) {
+ fragment = createFragment();
+ fm.beginTransaction()
+ .add(R.id.fragment_container, fragment)
+ .commit();
+ }
+ }
+
+}
diff --git a/Book Files/33_Locations/Locatr/app/src/main/res/layout/activity_fragment.xml b/Book Files/33_Locations/Locatr/app/src/main/res/layout/activity_fragment.xml
new file mode 100644
index 0000000..347d1d9
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/app/src/main/res/layout/activity_fragment.xml
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/Book Files/33_Locations/Locatr/app/src/main/res/layout/fragment_locatr.xml b/Book Files/33_Locations/Locatr/app/src/main/res/layout/fragment_locatr.xml
new file mode 100644
index 0000000..563978d
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/app/src/main/res/layout/fragment_locatr.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
diff --git a/Book Files/33_Locations/Locatr/app/src/main/res/menu/fragment_locatr.xml b/Book Files/33_Locations/Locatr/app/src/main/res/menu/fragment_locatr.xml
new file mode 100644
index 0000000..1fd1370
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/app/src/main/res/menu/fragment_locatr.xml
@@ -0,0 +1,9 @@
+
+
\ No newline at end of file
diff --git a/Book Files/33_Locations/Locatr/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Book Files/33_Locations/Locatr/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/Book Files/33_Locations/Locatr/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Book Files/33_Locations/Locatr/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Book Files/33_Locations/Locatr/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/Book Files/33_Locations/Locatr/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Book Files/33_Locations/Locatr/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Book Files/33_Locations/Locatr/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/Book Files/33_Locations/Locatr/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Book Files/33_Locations/Locatr/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Book Files/33_Locations/Locatr/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/Book Files/33_Locations/Locatr/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Book Files/33_Locations/Locatr/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Book Files/33_Locations/Locatr/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/Book Files/33_Locations/Locatr/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Book Files/33_Locations/Locatr/app/src/main/res/values-w820dp/dimens.xml b/Book Files/33_Locations/Locatr/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Book Files/33_Locations/Locatr/app/src/main/res/values/colors.xml b/Book Files/33_Locations/Locatr/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/Book Files/33_Locations/Locatr/app/src/main/res/values/dimens.xml b/Book Files/33_Locations/Locatr/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/Book Files/33_Locations/Locatr/app/src/main/res/values/strings.xml b/Book Files/33_Locations/Locatr/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..92f5a65
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/app/src/main/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+ Locatr
+
+ Find an image near you
+
diff --git a/Book Files/33_Locations/Locatr/app/src/main/res/values/styles.xml b/Book Files/33_Locations/Locatr/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Book Files/33_Locations/Locatr/app/src/test/java/com/bignerdranch/android/locatr/ExampleUnitTest.java b/Book Files/33_Locations/Locatr/app/src/test/java/com/bignerdranch/android/locatr/ExampleUnitTest.java
new file mode 100644
index 0000000..df7e954
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/app/src/test/java/com/bignerdranch/android/locatr/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.locatr;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/33_Locations/Locatr/build.gradle b/Book Files/33_Locations/Locatr/build.gradle
new file mode 100644
index 0000000..1ea4bd0
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Book Files/33_Locations/Locatr/gradle.properties b/Book Files/33_Locations/Locatr/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/Book Files/33_Locations/Locatr/gradle/wrapper/gradle-wrapper.jar b/Book Files/33_Locations/Locatr/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/Book Files/33_Locations/Locatr/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/33_Locations/Locatr/gradle/wrapper/gradle-wrapper.properties b/Book Files/33_Locations/Locatr/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a82392d
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/33_Locations/Locatr/gradlew b/Book Files/33_Locations/Locatr/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/33_Locations/Locatr/gradlew.bat b/Book Files/33_Locations/Locatr/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/33_Locations/Locatr/settings.gradle b/Book Files/33_Locations/Locatr/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/33_Locations/Locatr/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/33_Locations/support/.DS_Store b/Book Files/33_Locations/support/.DS_Store
new file mode 100755
index 0000000..762b331
Binary files /dev/null and b/Book Files/33_Locations/support/.DS_Store differ
diff --git a/Book Files/33_Locations/support/MockWalker/.gitignore b/Book Files/33_Locations/support/MockWalker/.gitignore
new file mode 100644
index 0000000..afbdab3
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/.gitignore
@@ -0,0 +1,6 @@
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
diff --git a/Book Files/33_Locations/support/MockWalker/.idea/.name b/Book Files/33_Locations/support/MockWalker/.idea/.name
new file mode 100644
index 0000000..fa0ee1b
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/.idea/.name
@@ -0,0 +1 @@
+Moonwalker
\ No newline at end of file
diff --git a/Book Files/33_Locations/support/MockWalker/.idea/compiler.xml b/Book Files/33_Locations/support/MockWalker/.idea/compiler.xml
new file mode 100644
index 0000000..217af47
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/.idea/compiler.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/33_Locations/support/MockWalker/.idea/vcs.xml b/Book Files/33_Locations/support/MockWalker/.idea/vcs.xml
new file mode 100644
index 0000000..ebabb34
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/.idea/vcs.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/Book Files/33_Locations/support/MockWalker/Mockwalker.iml b/Book Files/33_Locations/support/MockWalker/Mockwalker.iml
new file mode 100644
index 0000000..bb6cf7f
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/Mockwalker.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/33_Locations/support/MockWalker/app/.gitignore b/Book Files/33_Locations/support/MockWalker/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/33_Locations/support/MockWalker/app/app.iml b/Book Files/33_Locations/support/MockWalker/app/app.iml
new file mode 100644
index 0000000..4c270f6
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/app/app.iml
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Book Files/33_Locations/support/MockWalker/app/build.gradle b/Book Files/33_Locations/support/MockWalker/app/build.gradle
new file mode 100644
index 0000000..679e986
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/app/build.gradle
@@ -0,0 +1,28 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+
+ defaultConfig {
+ applicationId "com.bignerdranch.android.mockwalker"
+ minSdkVersion 16
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ compile 'com.google.android.gms:play-services-location:6.5.87'
+ compile 'com.jakewharton:butterknife:6.1.0'
+ compile 'io.reactivex:rxjava:1.0.8'
+}
diff --git a/Book Files/33_Locations/support/MockWalker/app/proguard-rules.pro b/Book Files/33_Locations/support/MockWalker/app/proguard-rules.pro
new file mode 100644
index 0000000..ccd85e1
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/bphillips/devtools/android-sdk-mac_x86/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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 *;
+#}
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/androidTest/java/com/bignerdranch/android/mockwalker/ApplicationTest.java b/Book Files/33_Locations/support/MockWalker/app/src/androidTest/java/com/bignerdranch/android/mockwalker/ApplicationTest.java
new file mode 100644
index 0000000..9c8ffee
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/app/src/androidTest/java/com/bignerdranch/android/mockwalker/ApplicationTest.java
@@ -0,0 +1,13 @@
+package com.bignerdranch.android.mockwalker;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * Testing Fundamentals
+ */
+public class ApplicationTest extends ApplicationTestCase {
+ public ApplicationTest() {
+ super(Application.class);
+ }
+}
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/AndroidManifest.xml b/Book Files/33_Locations/support/MockWalker/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..06f6339
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/app/src/main/AndroidManifest.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/java/com/bignerdranch/android/mockwalker/MockWalk.java b/Book Files/33_Locations/support/MockWalker/app/src/main/java/com/bignerdranch/android/mockwalker/MockWalk.java
new file mode 100644
index 0000000..ca80c75
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/app/src/main/java/com/bignerdranch/android/mockwalker/MockWalk.java
@@ -0,0 +1,252 @@
+package com.bignerdranch.android.mockwalker;
+
+import android.location.Location;
+import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.util.Log;
+
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.location.LocationServices;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MockWalk extends HandlerThread {
+ private static final String TAG = "MockWalk";
+ private boolean mEnabled = true;
+ private Handler mHandler;
+ private GoogleApiClient mClient;
+ private List mLocations;
+ private int mCurrentLocationIndex = 0;
+ private static final long PERIOD_LENGTH_MS = 1000;
+
+ private Runnable mLocationUpdate = new Runnable() {
+ @Override
+ public void run() {
+ postNextLocation();
+
+ scheduleNextUpdate();
+ }
+
+ };
+
+ public MockWalk(GoogleApiClient client) {
+ super("MockWalk");
+ mClient = client;
+ mLocations = getLocationData();
+ updateClientMockState();
+ }
+
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
+ public void setEnabled(boolean enabled) {
+ mEnabled = enabled;
+ if (isEnabled()) {
+ scheduleNextUpdate();
+ }
+
+ updateClientMockState();
+ }
+
+ private void updateClientMockState() {
+ LocationServices.FusedLocationApi
+ .setMockMode(mClient, isEnabled());
+ }
+
+ @Override
+ protected void onLooperPrepared() {
+ super.onLooperPrepared();
+
+ mHandler = new Handler();
+ scheduleNextUpdate();
+ }
+
+ @Override
+ public boolean quit() {
+ mHandler.removeCallbacks(mLocationUpdate);
+ return super.quit();
+ }
+
+ private void scheduleNextUpdate() {
+ if (isAlive() && !isInterrupted() && isEnabled()) {
+ mHandler.postDelayed(this.mLocationUpdate, PERIOD_LENGTH_MS);
+ }
+ }
+
+ private void postNextLocation() {
+ int nextIndex = (mCurrentLocationIndex + 1) % mLocations.size();
+
+ Location nextLocation = new Location(mLocations.get(nextIndex));
+ nextLocation.setTime(System.currentTimeMillis());
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ nextLocation.setElapsedRealtimeNanos(System.nanoTime());
+ }
+
+ LocationServices.FusedLocationApi
+ .setMockLocation(mClient, nextLocation);
+
+ Log.i(TAG, "New location: " + nextLocation);
+ mCurrentLocationIndex = nextIndex;
+ }
+
+ private Location newLocation(double lat, double lon, double alt) {
+ Location l = new Location("MOCK");
+ l.setLatitude(lat);
+ l.setLongitude(lon);
+ l.setAltitude(alt);
+ l.setAccuracy(0.5f);
+
+ return l;
+ }
+
+ private List getLocationData() {
+ List locations = new ArrayList();
+
+ locations.add(newLocation(33.751459, -84.3238770, 309));
+ locations.add(newLocation(33.7514752, -84.3238663, 304));
+ locations.add(newLocation(33.7514859, -84.3238610, 303));
+ locations.add(newLocation(33.7514752, -84.32385, 303));
+ locations.add(newLocation(33.7514859, -84.3237590, 287));
+ locations.add(newLocation(33.7514859, -84.3237376, 286));
+ locations.add(newLocation(33.7514805, -84.3237537, 286));
+ locations.add(newLocation(33.7514430, -84.3237537, 283));
+ locations.add(newLocation(33.7514108, -84.3237483, 286));
+ locations.add(newLocation(33.7514162, -84.3237590, 287));
+ locations.add(newLocation(33.7514269, -84.3237751, 287));
+ locations.add(newLocation(33.7514376, -84.3237859, 287));
+ locations.add(newLocation(33.7514698, -84.3238180, 283));
+ locations.add(newLocation(33.7514805, -84.3238288, 282));
+ locations.add(newLocation(33.7515181, -84.323887, 280));
+ locations.add(newLocation(33.7515234, -84.3238985, 279));
+ locations.add(newLocation(33.7515342, -84.3239146, 279));
+ locations.add(newLocation(33.7515449, -84.3239307, 279));
+ locations.add(newLocation(33.7515503, -84.3239468, 280));
+ locations.add(newLocation(33.7515556, -84.323957, 280));
+ locations.add(newLocation(33.751566, -84.3239682, 280));
+ locations.add(newLocation(33.7515717, -84.3239790, 280));
+ locations.add(newLocation(33.7515825, -84.3239897, 280));
+ locations.add(newLocation(33.75159323, -84.3240058, 280));
+ locations.add(newLocation(33.75160932, -84.3240165, 280));
+ locations.add(newLocation(33.75161468, -84.3240272, 280));
+ locations.add(newLocation(33.7510514, -84.3247300, 277));
+ locations.add(newLocation(33.751062, -84.3247246, 276));
+ locations.add(newLocation(33.75108897, -84.3247032, 276));
+ locations.add(newLocation(33.7510782, -84.3246924, 280));
+ locations.add(newLocation(33.7510836, -84.3247032, 282));
+ locations.add(newLocation(33.75109970, -84.324697, 281));
+ locations.add(newLocation(33.75112652, -84.324697, 283));
+ locations.add(newLocation(33.75115334, -84.3246924, 281));
+ locations.add(newLocation(33.7511640, -84.3246871, 284));
+ locations.add(newLocation(33.7511748, -84.3246871, 284));
+ locations.add(newLocation(33.7511855, -84.3246871, 284));
+ locations.add(newLocation(33.7511962, -84.3246871, 284));
+ locations.add(newLocation(33.751206, -84.3246871, 285));
+ locations.add(newLocation(33.7512177, -84.3246763, 286));
+ locations.add(newLocation(33.7512284, -84.3246763, 287));
+ locations.add(newLocation(33.7512391, -84.3246763, 288));
+ locations.add(newLocation(33.7512499, -84.3246817, 287));
+ locations.add(newLocation(33.75126, -84.3246763, 287));
+ locations.add(newLocation(33.7512713, -84.3246763, 287));
+ locations.add(newLocation(33.7512820, -84.3246710, 287));
+ locations.add(newLocation(33.7512981, -84.3246710, 287));
+ locations.add(newLocation(33.751314, -84.3246763, 286));
+ locations.add(newLocation(33.7513303, -84.3246710, 286));
+ locations.add(newLocation(33.75135183, -84.324660, 287));
+ locations.add(newLocation(33.75136792, -84.3246495, 287));
+ locations.add(newLocation(33.75137865, -84.3246495, 287));
+ locations.add(newLocation(33.75140011, -84.3246388, 286));
+ locations.add(newLocation(33.7514108, -84.3246281, 286));
+ locations.add(newLocation(33.7514269, -84.3246227, 286));
+ locations.add(newLocation(33.7514376, -84.3246120, 285));
+ locations.add(newLocation(33.7514537, -84.324606, 285));
+ locations.add(newLocation(33.7514644, -84.3246012, 285));
+ locations.add(newLocation(33.7514752, -84.3245959, 285));
+ locations.add(newLocation(33.7514805, -84.3245851, 285));
+ locations.add(newLocation(33.7515020, -84.3245798, 286));
+ locations.add(newLocation(33.751512, -84.3245744, 286));
+ locations.add(newLocation(33.7515234, -84.3245637, 286));
+ locations.add(newLocation(33.7515288, -84.324553, 287));
+ locations.add(newLocation(33.7515395, -84.3245476, 287));
+ locations.add(newLocation(33.7515503, -84.3245422, 287));
+ locations.add(newLocation(33.751566, -84.3245315, 288));
+ locations.add(newLocation(33.7515717, -84.3245208, 288));
+ locations.add(newLocation(33.7515825, -84.3245154, 288));
+ locations.add(newLocation(33.75158786, -84.324499, 290));
+ locations.add(newLocation(33.75160396, -84.3244940, 290));
+ locations.add(newLocation(33.75161468, -84.3244832, 291));
+ locations.add(newLocation(33.75162541, -84.3244725, 292));
+ locations.add(newLocation(33.75164151, -84.324461, 292));
+ locations.add(newLocation(33.75165224, -84.324445, 293));
+ locations.add(newLocation(33.7516629, -84.3244349, 293));
+ locations.add(newLocation(33.7516736, -84.3244242, 294));
+ locations.add(newLocation(33.7516790, -84.3244135, 294));
+ locations.add(newLocation(33.7516897, -84.3244028, 294));
+ locations.add(newLocation(33.7516897, -84.3243813, 294));
+ locations.add(newLocation(33.7516844, -84.3243598, 295));
+ locations.add(newLocation(33.7516790, -84.3243438, 295));
+ locations.add(newLocation(33.7516844, -84.3243277, 295));
+ locations.add(newLocation(33.7516790, -84.3243062, 295));
+ locations.add(newLocation(33.7516736, -84.3242955, 296));
+ locations.add(newLocation(33.7516683, -84.3242794, 297));
+ locations.add(newLocation(33.7516629, -84.3242686, 296));
+ locations.add(newLocation(33.75165224, -84.3242526, 297));
+ locations.add(newLocation(33.75164151, -84.3242365, 297));
+ locations.add(newLocation(33.75162541, -84.3242204, 297));
+ locations.add(newLocation(33.75162005, -84.3242043, 297));
+ locations.add(newLocation(33.75161468, -84.3241882, 298));
+ locations.add(newLocation(33.75160396, -84.3241775, 299));
+ locations.add(newLocation(33.75159859, -84.3241667, 298));
+ locations.add(newLocation(33.75159323, -84.324156, 299));
+ locations.add(newLocation(33.7515771, -84.3241453, 299));
+ locations.add(newLocation(33.751566, -84.3241345, 298));
+ locations.add(newLocation(33.7515556, -84.3241184, 298));
+ locations.add(newLocation(33.751566, -84.3241077, 297));
+ locations.add(newLocation(33.7515556, -84.3240863, 296));
+ locations.add(newLocation(33.7515503, -84.3240702, 296));
+ locations.add(newLocation(33.7515449, -84.3240541, 296));
+ locations.add(newLocation(33.7515288, -84.3240380, 297));
+ locations.add(newLocation(33.7515234, -84.3240272, 297));
+ locations.add(newLocation(33.7515181, -84.3240058, 297));
+ locations.add(newLocation(33.7515074, -84.323995, 297));
+ locations.add(newLocation(33.7515020, -84.3239790, 296));
+ locations.add(newLocation(33.7514913, -84.3239629, 295));
+ locations.add(newLocation(33.7514805, -84.3239521, 294));
+ locations.add(newLocation(33.7514644, -84.323941, 294));
+ locations.add(newLocation(33.7514537, -84.3239307, 293));
+ locations.add(newLocation(33.7514483, -84.3239146, 294));
+ locations.add(newLocation(33.7514483, -84.3238985, 293));
+ locations.add(newLocation(33.7514483, -84.3238770, 293));
+ locations.add(newLocation(33.7514483, -84.32385, 292));
+ locations.add(newLocation(33.7514537, -84.3238395, 291));
+ locations.add(newLocation(33.751459, -84.3238234, 291));
+ locations.add(newLocation(33.7514698, -84.3238019, 291));
+ locations.add(newLocation(33.7514805, -84.3237859, 291));
+ locations.add(newLocation(33.7514859, -84.3237751, 291));
+ locations.add(newLocation(33.7514913, -84.3237644, 291));
+ locations.add(newLocation(33.7514913, -84.3237483, 290));
+ locations.add(newLocation(33.7515020, -84.323742, 290));
+ locations.add(newLocation(33.7515074, -84.3237322, 290));
+ locations.add(newLocation(33.7515181, -84.3237268, 290));
+ locations.add(newLocation(33.7515288, -84.3237161, 290));
+ locations.add(newLocation(33.7515342, -84.323705, 290));
+ locations.add(newLocation(33.7515181, -84.3237000, 290));
+ locations.add(newLocation(33.7515074, -84.3237161, 290));
+ locations.add(newLocation(33.7514966, -84.3237376, 290));
+ locations.add(newLocation(33.7514913, -84.3237537, 290));
+ locations.add(newLocation(33.7514859, -84.3237751, 290));
+ locations.add(newLocation(33.7514966, -84.3237751, 290));
+ locations.add(newLocation(33.7515020, -84.323796, 290));
+ locations.add(newLocation(33.751512, -84.323796, 290));
+ locations.add(newLocation(33.7515234, -84.3238127, 289));
+ locations.add(newLocation(33.7514966, -84.3237912, 289));
+ locations.add(newLocation(33.7514859, -84.3237805, 289));
+ locations.add(newLocation(33.751459, -84.3237698, 289));
+ locations.add(newLocation(33.7514537, -84.3237805, 289));
+ locations.add(newLocation(33.7514430, -84.3237751, 290));
+
+ return locations;
+ }
+}
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/java/com/bignerdranch/android/mockwalker/MockWalker.java b/Book Files/33_Locations/support/MockWalker/app/src/main/java/com/bignerdranch/android/mockwalker/MockWalker.java
new file mode 100644
index 0000000..ae7f06d
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/app/src/main/java/com/bignerdranch/android/mockwalker/MockWalker.java
@@ -0,0 +1,76 @@
+package com.bignerdranch.android.mockwalker;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.location.LocationServices;
+
+import rx.Observable;
+import rx.subjects.PublishSubject;
+
+public class MockWalker implements GoogleApiClient.ConnectionCallbacks {
+ private static MockWalker sMockWalker;
+
+ public static synchronized MockWalker get(Context context) {
+ if (sMockWalker == null) {
+ sMockWalker = new MockWalker(context);
+ }
+
+ return sMockWalker;
+ }
+
+ private PublishSubject mChanges;
+ private Context mContext;
+ private final GoogleApiClient mClient;
+ private boolean mStarted;
+ private MockWalk mMockWalk;
+
+ private MockWalker(Context context) {
+ mContext = context.getApplicationContext();
+ mChanges = PublishSubject.create();
+ mClient = new GoogleApiClient.Builder(mContext)
+ .addApi(LocationServices.API)
+ .addConnectionCallbacks(this)
+ .build();
+ mClient.connect();
+ }
+
+ public Observable getChanges() {
+ return mChanges;
+ }
+
+ @Override
+ public void onConnected(Bundle bundle) {
+ syncWalkState();
+ }
+
+ public void setStarted(boolean started) {
+ mStarted = started;
+ syncWalkState();
+ mChanges.onNext(this);
+ }
+
+ public boolean isStarted() {
+ return mStarted;
+ }
+
+ private void syncWalkState() {
+ if (!mStarted && mMockWalk != null) {
+ mMockWalk.quit();
+ mMockWalk = null;
+ } else if (!mStarted && mMockWalk == null) {
+ // all good
+ } else if (mStarted && mMockWalk == null && !mClient.isConnected()) {
+ // all good
+ } else if (mStarted && mMockWalk == null && mClient.isConnected()) {
+ mMockWalk = new MockWalk(mClient);
+ mMockWalk.start();
+ }
+ }
+
+ @Override
+ public void onConnectionSuspended(int i) {
+
+ }
+}
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/java/com/bignerdranch/android/mockwalker/MockWalkerActivity.java b/Book Files/33_Locations/support/MockWalker/app/src/main/java/com/bignerdranch/android/mockwalker/MockWalkerActivity.java
new file mode 100644
index 0000000..f3affd1
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/app/src/main/java/com/bignerdranch/android/mockwalker/MockWalkerActivity.java
@@ -0,0 +1,40 @@
+package com.bignerdranch.android.mockwalker;
+
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.support.v4.app.Fragment;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.GooglePlayServicesUtil;
+
+
+public class MockWalkerActivity extends SingleFragmentActivity {
+ private static final int REQUEST_ERROR = 0;
+
+ @Override
+ protected Fragment createFragment() {
+ return new MockWalkerFragment();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ int errorCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
+
+ if (errorCode != ConnectionResult.SUCCESS) {
+ Dialog errorDialog = GooglePlayServicesUtil
+ .getErrorDialog(errorCode, this, REQUEST_ERROR,
+ new DialogInterface.OnCancelListener() {
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ // Leave if services are unavailable.
+ finish();
+ }
+ });
+
+ errorDialog.show();
+ }
+ }
+}
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/java/com/bignerdranch/android/mockwalker/MockWalkerFragment.java b/Book Files/33_Locations/support/MockWalker/app/src/main/java/com/bignerdranch/android/mockwalker/MockWalkerFragment.java
new file mode 100644
index 0000000..79fc3f0
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/app/src/main/java/com/bignerdranch/android/mockwalker/MockWalkerFragment.java
@@ -0,0 +1,69 @@
+package com.bignerdranch.android.mockwalker;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CompoundButton;
+
+import butterknife.ButterKnife;
+import butterknife.InjectView;
+import butterknife.OnClick;
+import rx.functions.Action1;
+import rx.subscriptions.CompositeSubscription;
+
+public class MockWalkerFragment extends Fragment {
+ @InjectView(R.id.start_button) CompoundButton mStartButton;
+ private CompositeSubscription mServiceSubscriptions = new CompositeSubscription();
+ private Intent mServiceIntent;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.fragment_mockwalker, container, false);
+
+ ButterKnife.inject(this, v);
+
+ mServiceIntent = new Intent(getActivity(), MockWalkerService.class);
+
+ mServiceSubscriptions.add(MockWalker.get(getActivity()).getChanges()
+ .subscribe(new Action1() {
+ @Override
+ public void call(MockWalker mockWalker) {
+ updateUI();
+ }
+ }));
+
+ updateUI();
+
+ return v;
+ }
+
+ @OnClick(R.id.start_button)
+ public void onStartButtonClick() {
+ MockWalker mockWalker = MockWalker.get(getActivity());
+
+ if (mockWalker.isStarted()) {
+ getActivity().stopService(mServiceIntent);
+ } else {
+ getActivity().startService(mServiceIntent);
+ }
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ mServiceSubscriptions.unsubscribe();
+ }
+
+ private void updateUI() {
+ MockWalker mockWalker = MockWalker.get(getActivity());
+ if (mockWalker.isStarted()) {
+ mStartButton.setChecked(true);
+ } else {
+ mStartButton.setChecked(false);
+ }
+ }
+}
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/java/com/bignerdranch/android/mockwalker/MockWalkerService.java b/Book Files/33_Locations/support/MockWalker/app/src/main/java/com/bignerdranch/android/mockwalker/MockWalkerService.java
new file mode 100644
index 0000000..e8d568c
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/app/src/main/java/com/bignerdranch/android/mockwalker/MockWalkerService.java
@@ -0,0 +1,53 @@
+package com.bignerdranch.android.mockwalker;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.support.v4.app.NotificationCompat;
+
+public class MockWalkerService extends Service {
+ private static final int NOTIFICATION_ID = 1;
+ private static final int PENDING_SHUTDOWN_ID = 1;
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ return Service.START_STICKY;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ Intent shutdownIntent = new Intent(this, ShutdownReceiver.class);
+ PendingIntent shutdownPI = PendingIntent.getBroadcast(
+ this, PENDING_SHUTDOWN_ID, shutdownIntent, 0
+ );
+ Notification notification = new NotificationCompat.Builder(this)
+ .setSmallIcon(android.R.drawable.ic_dialog_map)
+ .setContentTitle(getString(R.string.notification_title))
+ .setContentText(getString(R.string.notification_text))
+ .setTicker(getString(R.string.app_name))
+ .setContentIntent(shutdownPI)
+ .build();
+
+ startForeground(NOTIFICATION_ID, notification);
+ MockWalker.get(this).setStarted(true);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+
+ stopForeground(true);
+ MockWalker.get(this).setStarted(false);
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ throw new UnsupportedOperationException("" + getClass().getName() + " is not a bindable service");
+ }
+
+
+}
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/java/com/bignerdranch/android/mockwalker/ShutdownReceiver.java b/Book Files/33_Locations/support/MockWalker/app/src/main/java/com/bignerdranch/android/mockwalker/ShutdownReceiver.java
new file mode 100644
index 0000000..edefd57
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/app/src/main/java/com/bignerdranch/android/mockwalker/ShutdownReceiver.java
@@ -0,0 +1,14 @@
+package com.bignerdranch.android.mockwalker;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class ShutdownReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Intent shutdown = new Intent(context, MockWalkerService.class);
+ context.stopService(shutdown);
+ }
+}
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/java/com/bignerdranch/android/mockwalker/SingleFragmentActivity.java b/Book Files/33_Locations/support/MockWalker/app/src/main/java/com/bignerdranch/android/mockwalker/SingleFragmentActivity.java
new file mode 100644
index 0000000..2343c36
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/app/src/main/java/com/bignerdranch/android/mockwalker/SingleFragmentActivity.java
@@ -0,0 +1,28 @@
+package com.bignerdranch.android.mockwalker;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.AppCompatActivity;
+
+public abstract class SingleFragmentActivity extends AppCompatActivity {
+
+ protected abstract Fragment createFragment();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_fragment);
+
+ FragmentManager fm = getSupportFragmentManager();
+ Fragment fragment = fm.findFragmentById(R.id.fragment_container);
+
+ if (fragment == null) {
+ fragment = createFragment();
+ fm.beginTransaction()
+ .add(R.id.fragment_container, fragment)
+ .commit();
+ }
+ }
+
+}
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-hdpi/start_gps.png b/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-hdpi/start_gps.png
new file mode 100755
index 0000000..434e5c8
Binary files /dev/null and b/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-hdpi/start_gps.png differ
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-hdpi/stop_gps.png b/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-hdpi/stop_gps.png
new file mode 100755
index 0000000..e74e5f7
Binary files /dev/null and b/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-hdpi/stop_gps.png differ
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-ldpi/start_gps.png b/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-ldpi/start_gps.png
new file mode 100755
index 0000000..304b240
Binary files /dev/null and b/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-ldpi/start_gps.png differ
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-ldpi/stop_gps.png b/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-ldpi/stop_gps.png
new file mode 100755
index 0000000..8746be6
Binary files /dev/null and b/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-ldpi/stop_gps.png differ
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-mdpi/start_gps.png b/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-mdpi/start_gps.png
new file mode 100755
index 0000000..3f64861
Binary files /dev/null and b/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-mdpi/start_gps.png differ
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-mdpi/stop_gps.png b/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-mdpi/stop_gps.png
new file mode 100755
index 0000000..070253a
Binary files /dev/null and b/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-mdpi/stop_gps.png differ
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-xhdpi/start_gps.png b/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-xhdpi/start_gps.png
new file mode 100755
index 0000000..6683fc3
Binary files /dev/null and b/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-xhdpi/start_gps.png differ
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-xhdpi/stop_gps.png b/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-xhdpi/stop_gps.png
new file mode 100755
index 0000000..d1243a7
Binary files /dev/null and b/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-xhdpi/stop_gps.png differ
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-xxhdpi/start_gps.png b/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-xxhdpi/start_gps.png
new file mode 100755
index 0000000..e2503f0
Binary files /dev/null and b/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-xxhdpi/start_gps.png differ
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-xxhdpi/stop_gps.png b/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-xxhdpi/stop_gps.png
new file mode 100755
index 0000000..4f52243
Binary files /dev/null and b/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-xxhdpi/stop_gps.png differ
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-xxxhdpi/start_gps.png b/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-xxxhdpi/start_gps.png
new file mode 100755
index 0000000..867e15f
Binary files /dev/null and b/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-xxxhdpi/start_gps.png differ
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-xxxhdpi/stop_gps.png b/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-xxxhdpi/stop_gps.png
new file mode 100755
index 0000000..a857a1d
Binary files /dev/null and b/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable-xxxhdpi/stop_gps.png differ
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable/start_stop.xml b/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable/start_stop.xml
new file mode 100644
index 0000000..1409c8a
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/app/src/main/res/drawable/start_stop.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/res/layout/activity_fragment.xml b/Book Files/33_Locations/support/MockWalker/app/src/main/res/layout/activity_fragment.xml
new file mode 100644
index 0000000..511f5f4
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/app/src/main/res/layout/activity_fragment.xml
@@ -0,0 +1,5 @@
+
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/res/layout/fragment_mockwalker.xml b/Book Files/33_Locations/support/MockWalker/app/src/main/res/layout/fragment_mockwalker.xml
new file mode 100644
index 0000000..54f15c8
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/app/src/main/res/layout/fragment_mockwalker.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/res/menu/menu_moonwalker.xml b/Book Files/33_Locations/support/MockWalker/app/src/main/res/menu/menu_moonwalker.xml
new file mode 100644
index 0000000..199a726
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/app/src/main/res/menu/menu_moonwalker.xml
@@ -0,0 +1,9 @@
+
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Book Files/33_Locations/support/MockWalker/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100755
index 0000000..901cd2f
Binary files /dev/null and b/Book Files/33_Locations/support/MockWalker/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/res/mipmap-ldpi/ic_launcher.png b/Book Files/33_Locations/support/MockWalker/app/src/main/res/mipmap-ldpi/ic_launcher.png
new file mode 100755
index 0000000..2202187
Binary files /dev/null and b/Book Files/33_Locations/support/MockWalker/app/src/main/res/mipmap-ldpi/ic_launcher.png differ
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Book Files/33_Locations/support/MockWalker/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100755
index 0000000..432038c
Binary files /dev/null and b/Book Files/33_Locations/support/MockWalker/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Book Files/33_Locations/support/MockWalker/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100755
index 0000000..540bac8
Binary files /dev/null and b/Book Files/33_Locations/support/MockWalker/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Book Files/33_Locations/support/MockWalker/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100755
index 0000000..fff4e37
Binary files /dev/null and b/Book Files/33_Locations/support/MockWalker/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Book Files/33_Locations/support/MockWalker/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100755
index 0000000..0ac3ade
Binary files /dev/null and b/Book Files/33_Locations/support/MockWalker/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/res/values-w820dp/dimens.xml b/Book Files/33_Locations/support/MockWalker/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/res/values/dimens.xml b/Book Files/33_Locations/support/MockWalker/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/res/values/strings.xml b/Book Files/33_Locations/support/MockWalker/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..b0182e5
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/app/src/main/res/values/strings.xml
@@ -0,0 +1,8 @@
+
+ MockWalker
+
+ Hello world!
+ Settings
+ MockWalker running
+ Tap to stop
+
diff --git a/Book Files/33_Locations/support/MockWalker/app/src/main/res/values/styles.xml b/Book Files/33_Locations/support/MockWalker/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..766ab99
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/app/src/main/res/values/styles.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
diff --git a/Book Files/33_Locations/support/MockWalker/build.gradle b/Book Files/33_Locations/support/MockWalker/build.gradle
new file mode 100644
index 0000000..c6f2f50
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/build.gradle
@@ -0,0 +1,19 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
diff --git a/Book Files/33_Locations/support/MockWalker/gradle.properties b/Book Files/33_Locations/support/MockWalker/gradle.properties
new file mode 100644
index 0000000..1d3591c
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/gradle.properties
@@ -0,0 +1,18 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
\ No newline at end of file
diff --git a/Book Files/33_Locations/support/MockWalker/gradle/wrapper/gradle-wrapper.jar b/Book Files/33_Locations/support/MockWalker/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
Binary files /dev/null and b/Book Files/33_Locations/support/MockWalker/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/33_Locations/support/MockWalker/gradle/wrapper/gradle-wrapper.properties b/Book Files/33_Locations/support/MockWalker/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..71d2c80
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/33_Locations/support/MockWalker/gradlew b/Book Files/33_Locations/support/MockWalker/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/33_Locations/support/MockWalker/gradlew.bat b/Book Files/33_Locations/support/MockWalker/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/33_Locations/support/MockWalker/settings.gradle b/Book Files/33_Locations/support/MockWalker/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/33_Locations/support/MockWalker/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/34_Maps/.DS_Store b/Book Files/34_Maps/.DS_Store
new file mode 100755
index 0000000..8f36eec
Binary files /dev/null and b/Book Files/34_Maps/.DS_Store differ
diff --git a/Book Files/34_Maps/LICENSE.txt b/Book Files/34_Maps/LICENSE.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/Book Files/34_Maps/LICENSE.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Book Files/34_Maps/Locatr/.gitignore b/Book Files/34_Maps/Locatr/.gitignore
new file mode 100644
index 0000000..39fb081
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/.gitignore
@@ -0,0 +1,9 @@
+*.iml
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/Book Files/34_Maps/Locatr/.idea/compiler.xml b/Book Files/34_Maps/Locatr/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/34_Maps/Locatr/.idea/copyright/profiles_settings.xml b/Book Files/34_Maps/Locatr/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Book Files/34_Maps/Locatr/.idea/encodings.xml b/Book Files/34_Maps/Locatr/.idea/encodings.xml
new file mode 100644
index 0000000..97626ba
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/.idea/encodings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/34_Maps/Locatr/.idea/gradle.xml b/Book Files/34_Maps/Locatr/.idea/gradle.xml
new file mode 100644
index 0000000..0e23f8e
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/.idea/gradle.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/34_Maps/Locatr/.idea/misc.xml b/Book Files/34_Maps/Locatr/.idea/misc.xml
new file mode 100644
index 0000000..fbb6828
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/.idea/misc.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/34_Maps/Locatr/.idea/modules.xml b/Book Files/34_Maps/Locatr/.idea/modules.xml
new file mode 100644
index 0000000..7fc7a24
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/.idea/modules.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/34_Maps/Locatr/.idea/runConfigurations.xml b/Book Files/34_Maps/Locatr/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/34_Maps/Locatr/app/.gitignore b/Book Files/34_Maps/Locatr/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/34_Maps/Locatr/app/build.gradle b/Book Files/34_Maps/Locatr/app/build.gradle
new file mode 100644
index 0000000..0ab87a1
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/app/build.gradle
@@ -0,0 +1,31 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+ defaultConfig {
+ applicationId "com.bignerdranch.android.locatr"
+ minSdkVersion 19
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ compile 'com.google.android.gms:play-services-location:9.8.0'
+ compile 'com.google.android.gms:play-services-maps:9.8.0'
+ testCompile 'junit:junit:4.12'
+}
diff --git a/Book Files/34_Maps/Locatr/app/proguard-rules.pro b/Book Files/34_Maps/Locatr/app/proguard-rules.pro
new file mode 100644
index 0000000..cf5610c
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/bphillips/devtools/android-sdk-macosx/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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 *;
+#}
diff --git a/Book Files/34_Maps/Locatr/app/src/androidTest/java/com/bignerdranch/android/locatr/ExampleInstrumentedTest.java b/Book Files/34_Maps/Locatr/app/src/androidTest/java/com/bignerdranch/android/locatr/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..82fb339
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/app/src/androidTest/java/com/bignerdranch/android/locatr/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.bignerdranch.android.locatr;
+
+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.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.bignerdranch.android.locatr", appContext.getPackageName());
+ }
+}
diff --git a/Book Files/34_Maps/Locatr/app/src/debug/res/values/google_maps_api.xml b/Book Files/34_Maps/Locatr/app/src/debug/res/values/google_maps_api.xml
new file mode 100644
index 0000000..b68f914
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/app/src/debug/res/values/google_maps_api.xml
@@ -0,0 +1,20 @@
+
+
+ AIzaSyClrnnYZEx0iYmJkIc0K4rdObrXcFkLl-U
+
diff --git a/Book Files/34_Maps/Locatr/app/src/main/AndroidManifest.xml b/Book Files/34_Maps/Locatr/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..993125d
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/app/src/main/AndroidManifest.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Book Files/34_Maps/Locatr/app/src/main/java/com/bignerdranch/android/locatr/FlickrFetchr.java b/Book Files/34_Maps/Locatr/app/src/main/java/com/bignerdranch/android/locatr/FlickrFetchr.java
new file mode 100644
index 0000000..c6cd65a
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/app/src/main/java/com/bignerdranch/android/locatr/FlickrFetchr.java
@@ -0,0 +1,137 @@
+package com.bignerdranch.android.locatr;
+
+import android.location.Location;
+import android.net.Uri;
+import android.util.Log;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+public class FlickrFetchr {
+ private static final String TAG = "FlickrFetchr";
+
+ private static final String API_KEY = "REPLACE_ME_WITH_A_REAL_KEY";
+ private static final String FETCH_RECENTS_METHOD = "flickr.photos.getRecent";
+ private static final String SEARCH_METHOD = "flickr.photos.search";
+ private static final Uri ENDPOINT = Uri
+ .parse("https://api.flickr.com/services/rest/")
+ .buildUpon()
+ .appendQueryParameter("api_key", API_KEY)
+ .appendQueryParameter("format", "json")
+ .appendQueryParameter("nojsoncallback", "1")
+ .appendQueryParameter("extras", "url_s,geo")
+ .build();
+
+ public byte[] getUrlBytes(String urlSpec) throws IOException {
+ URL url = new URL(urlSpec);
+ HttpURLConnection connection = (HttpURLConnection)url.openConnection();
+ try {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ InputStream in = connection.getInputStream();
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ throw new IOException(connection.getResponseMessage() +
+ ": with " +
+ urlSpec);
+ }
+ int bytesRead = 0;
+ byte[] buffer = new byte[1024];
+ while ((bytesRead = in.read(buffer)) > 0) {
+ out.write(buffer, 0, bytesRead);
+ }
+ out.close();
+ return out.toByteArray();
+ } finally {
+ connection.disconnect();
+ }
+ }
+
+ public String getUrlString(String urlSpec) throws IOException {
+ return new String(getUrlBytes(urlSpec));
+ }
+
+ public List fetchRecentPhotos() {
+ String url = buildUrl(FETCH_RECENTS_METHOD, null);
+ return downloadGalleryItems(url);
+ }
+
+ public List searchPhotos(String query) {
+ String url = buildUrl(SEARCH_METHOD, query);
+ return downloadGalleryItems(url);
+ }
+
+ public List searchPhotos(Location location) {
+ String url = buildUrl(location);
+ return downloadGalleryItems(url);
+ }
+
+ private List downloadGalleryItems(String url) {
+ List items = new ArrayList<>();
+
+ try {
+ String jsonString = getUrlString(url);
+ Log.i(TAG, "Received JSON: " + jsonString);
+ JSONObject jsonBody = new JSONObject(jsonString);
+ parseItems(items, jsonBody);
+ } catch (IOException ioe) {
+ Log.e(TAG, "Failed to fetch items", ioe);
+ } catch (JSONException je) {
+ Log.e(TAG, "Failed to parse JSON", je);
+ }
+
+ return items;
+ }
+
+ private String buildUrl(String method, String query) {
+ Uri.Builder uriBuilder = ENDPOINT.buildUpon()
+ .appendQueryParameter("method", method);
+
+ if (method.equals(SEARCH_METHOD)) {
+ uriBuilder.appendQueryParameter("text", query);
+ }
+
+ return uriBuilder.build().toString();
+ }
+
+ private String buildUrl(Location location) {
+ return ENDPOINT.buildUpon()
+ .appendQueryParameter("method", SEARCH_METHOD)
+ .appendQueryParameter("lat", "" + location.getLatitude())
+ .appendQueryParameter("lon", "" + location.getLongitude())
+ .build().toString();
+ }
+
+ private void parseItems(List items, JSONObject jsonBody)
+ throws IOException, JSONException {
+
+ JSONObject photosJsonObject = jsonBody.getJSONObject("photos");
+ JSONArray photoJsonArray = photosJsonObject.getJSONArray("photo");
+
+ for (int i = 0; i < photoJsonArray.length(); i++) {
+ JSONObject photoJsonObject = photoJsonArray.getJSONObject(i);
+
+ GalleryItem item = new GalleryItem();
+ item.setId(photoJsonObject.getString("id"));
+ item.setCaption(photoJsonObject.getString("title"));
+
+ if (!photoJsonObject.has("url_s")) {
+ continue;
+ }
+
+ item.setUrl(photoJsonObject.getString("url_s"));
+ item.setOwner(photoJsonObject.getString("owner"));
+ item.setLat(photoJsonObject.getDouble("latitude"));
+ item.setLon(photoJsonObject.getDouble("longitude"));
+ items.add(item);
+ }
+ }
+
+}
diff --git a/Book Files/34_Maps/Locatr/app/src/main/java/com/bignerdranch/android/locatr/GalleryItem.java b/Book Files/34_Maps/Locatr/app/src/main/java/com/bignerdranch/android/locatr/GalleryItem.java
new file mode 100644
index 0000000..e8403d2
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/app/src/main/java/com/bignerdranch/android/locatr/GalleryItem.java
@@ -0,0 +1,73 @@
+package com.bignerdranch.android.locatr;
+
+import android.net.Uri;
+
+public class GalleryItem {
+ private String mCaption;
+ private String mId;
+ private String mUrl;
+ private String mOwner;
+ private double mLat;
+ private double mLon;
+
+ public String getCaption() {
+ return mCaption;
+ }
+
+ public void setCaption(String caption) {
+ mCaption = caption;
+ }
+
+ public String getId() {
+ return mId;
+ }
+
+ public void setId(String id) {
+ mId = id;
+ }
+
+ public String getUrl() {
+ return mUrl;
+ }
+
+ public void setUrl(String url) {
+ mUrl = url;
+ }
+
+ public String getOwner() {
+ return mOwner;
+ }
+
+ public void setOwner(String owner) {
+ mOwner = owner;
+ }
+
+ public Uri getPhotoPageUri() {
+ return Uri.parse("http://www.flickr.com/photos/")
+ .buildUpon()
+ .appendPath(mOwner)
+ .appendPath(mId)
+ .build();
+ }
+
+ public double getLat() {
+ return mLat;
+ }
+
+ public void setLat(double lat) {
+ mLat = lat;
+ }
+
+ public double getLon() {
+ return mLon;
+ }
+
+ public void setLon(double lon) {
+ mLon = lon;
+ }
+
+ @Override
+ public String toString() {
+ return mCaption;
+ }
+}
diff --git a/Book Files/34_Maps/Locatr/app/src/main/java/com/bignerdranch/android/locatr/LocatrActivity.java b/Book Files/34_Maps/Locatr/app/src/main/java/com/bignerdranch/android/locatr/LocatrActivity.java
new file mode 100644
index 0000000..1dc8d0a
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/app/src/main/java/com/bignerdranch/android/locatr/LocatrActivity.java
@@ -0,0 +1,42 @@
+package com.bignerdranch.android.locatr;
+
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.support.v4.app.Fragment;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.GoogleApiAvailability;
+
+public class LocatrActivity extends SingleFragmentActivity {
+ private static final int REQUEST_ERROR = 0;
+
+ @Override
+ protected Fragment createFragment() {
+ return LocatrFragment.newInstance();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance();
+ int errorCode = apiAvailability.isGooglePlayServicesAvailable(this);
+
+ if (errorCode != ConnectionResult.SUCCESS) {
+ Dialog errorDialog = apiAvailability.getErrorDialog(this,
+ errorCode,
+ REQUEST_ERROR,
+ new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialogInterface) {
+ // Leave if services are unavailable.
+ finish();
+ }
+ });
+
+ errorDialog.show();
+ }
+ }
+}
diff --git a/Book Files/34_Maps/Locatr/app/src/main/java/com/bignerdranch/android/locatr/LocatrFragment.java b/Book Files/34_Maps/Locatr/app/src/main/java/com/bignerdranch/android/locatr/LocatrFragment.java
new file mode 100644
index 0000000..9c9b863
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/app/src/main/java/com/bignerdranch/android/locatr/LocatrFragment.java
@@ -0,0 +1,225 @@
+package com.bignerdranch.android.locatr;
+
+import android.Manifest;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.location.Location;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v4.content.ContextCompat;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+
+import com.google.android.gms.common.api.GoogleApiClient;
+import com.google.android.gms.location.LocationListener;
+import com.google.android.gms.location.LocationRequest;
+import com.google.android.gms.location.LocationServices;
+import com.google.android.gms.maps.CameraUpdate;
+import com.google.android.gms.maps.CameraUpdateFactory;
+import com.google.android.gms.maps.GoogleMap;
+import com.google.android.gms.maps.OnMapReadyCallback;
+import com.google.android.gms.maps.SupportMapFragment;
+import com.google.android.gms.maps.model.BitmapDescriptor;
+import com.google.android.gms.maps.model.BitmapDescriptorFactory;
+import com.google.android.gms.maps.model.LatLng;
+import com.google.android.gms.maps.model.LatLngBounds;
+import com.google.android.gms.maps.model.MarkerOptions;
+
+import java.io.IOException;
+import java.util.List;
+
+
+public class LocatrFragment extends SupportMapFragment {
+ private static final String TAG = "LocatrFragment";
+ private static final String[] LOCATION_PERMISSIONS = new String[]{
+ Manifest.permission.ACCESS_FINE_LOCATION,
+ Manifest.permission.ACCESS_COARSE_LOCATION,
+ };
+ private static final int REQUEST_LOCATION_PERMISSIONS = 0;
+
+ private GoogleApiClient mClient;
+ private GoogleMap mMap;
+ private Bitmap mMapImage;
+ private GalleryItem mMapItem;
+ private Location mCurrentLocation;
+
+ public static LocatrFragment newInstance() {
+ return new LocatrFragment();
+ }
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+
+ mClient = new GoogleApiClient.Builder(getActivity()).addApi(LocationServices.API)
+ .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
+ @Override
+ public void onConnected(@Nullable Bundle bundle) {
+ getActivity().invalidateOptionsMenu();
+ }
+
+ @Override
+ public void onConnectionSuspended(int i) {
+
+ }
+ })
+ .build();
+
+ getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(GoogleMap googleMap) {
+ mMap = googleMap;
+ updateUI();
+ }
+ });
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ getActivity().invalidateOptionsMenu();
+ mClient.connect();
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+
+ mClient.disconnect();
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ inflater.inflate(R.menu.fragment_locatr, menu);
+
+ MenuItem searchItem = menu.findItem(R.id.action_locate);
+ searchItem.setEnabled(mClient.isConnected());
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.action_locate:
+ if (hasLocationPermission()) {
+ findImage();
+ } else {
+ requestPermissions(LOCATION_PERMISSIONS,
+ REQUEST_LOCATION_PERMISSIONS);
+ }
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String[] permissions,
+ int[] grantResults) {
+ switch (requestCode) {
+ case REQUEST_LOCATION_PERMISSIONS:
+ if (hasLocationPermission()) {
+ findImage();
+ }
+ default:
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ }
+ }
+
+ private void findImage() {
+ LocationRequest request = LocationRequest.create();
+ request.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
+ request.setNumUpdates(1);
+ request.setInterval(0);
+ LocationServices.FusedLocationApi
+ .requestLocationUpdates(mClient, request, new LocationListener() {
+ @Override
+ public void onLocationChanged(Location location) {
+ Log.i(TAG, "Got a fix: " + location);
+ new SearchTask().execute(location);
+ }
+ });
+ }
+
+ private boolean hasLocationPermission() {
+ int result = ContextCompat
+ .checkSelfPermission(getActivity(), LOCATION_PERMISSIONS[0]);
+ return result == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private void updateUI() {
+ if (mMap == null || mMapImage == null) {
+ return;
+ }
+
+ LatLng itemPoint = new LatLng(mMapItem.getLat(), mMapItem.getLon());
+ LatLng myPoint = new LatLng(
+ mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude());
+
+ BitmapDescriptor itemBitmap = BitmapDescriptorFactory.fromBitmap(mMapImage);
+ MarkerOptions itemMarker = new MarkerOptions()
+ .position(itemPoint)
+ .icon(itemBitmap);
+ MarkerOptions myMarker = new MarkerOptions()
+ .position(myPoint);
+ mMap.clear();
+ mMap.addMarker(itemMarker);
+ mMap.addMarker(myMarker);
+
+ LatLngBounds bounds = new LatLngBounds.Builder()
+ .include(itemPoint)
+ .include(myPoint)
+ .build();
+
+ int margin = getResources().getDimensionPixelSize(R.dimen.map_inset_margin);
+ CameraUpdate update = CameraUpdateFactory.newLatLngBounds(bounds, margin);
+ mMap.animateCamera(update);
+ }
+
+ private class SearchTask extends AsyncTask {
+ private GalleryItem mGalleryItem;
+ private Bitmap mBitmap;
+ private Location mLocation;
+
+ @Override
+ protected Void doInBackground(Location... params) {
+ mLocation = params[0];
+ FlickrFetchr fetchr = new FlickrFetchr();
+ List items = fetchr.searchPhotos(params[0]);
+
+ if (items.size() == 0) {
+ return null;
+ }
+
+ mGalleryItem = items.get(0);
+
+ try {
+ byte[] bytes = fetchr.getUrlBytes(mGalleryItem.getUrl());
+ mBitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
+ } catch (IOException ioe) {
+ Log.i(TAG, "Unable to decode bitmap", ioe);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ mMapImage = mBitmap;
+ mMapItem = mGalleryItem;
+ mCurrentLocation = mLocation;
+
+ updateUI();
+ }
+ }
+}
diff --git a/Book Files/34_Maps/Locatr/app/src/main/java/com/bignerdranch/android/locatr/SingleFragmentActivity.java b/Book Files/34_Maps/Locatr/app/src/main/java/com/bignerdranch/android/locatr/SingleFragmentActivity.java
new file mode 100644
index 0000000..5318df8
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/app/src/main/java/com/bignerdranch/android/locatr/SingleFragmentActivity.java
@@ -0,0 +1,28 @@
+package com.bignerdranch.android.locatr;
+
+import android.os.Bundle;
+import android.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v7.app.AppCompatActivity;
+
+public abstract class SingleFragmentActivity extends AppCompatActivity {
+
+ protected abstract Fragment createFragment();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_fragment);
+
+ FragmentManager fm = getSupportFragmentManager();
+ Fragment fragment = fm.findFragmentById(R.id.fragment_container);
+
+ if (fragment == null) {
+ fragment = createFragment();
+ fm.beginTransaction()
+ .add(R.id.fragment_container, fragment)
+ .commit();
+ }
+ }
+
+}
diff --git a/Book Files/34_Maps/Locatr/app/src/main/res/layout/activity_fragment.xml b/Book Files/34_Maps/Locatr/app/src/main/res/layout/activity_fragment.xml
new file mode 100644
index 0000000..347d1d9
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/app/src/main/res/layout/activity_fragment.xml
@@ -0,0 +1,5 @@
+
\ No newline at end of file
diff --git a/Book Files/34_Maps/Locatr/app/src/main/res/layout/activity_maps.xml b/Book Files/34_Maps/Locatr/app/src/main/res/layout/activity_maps.xml
new file mode 100644
index 0000000..fc1990f
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/app/src/main/res/layout/activity_maps.xml
@@ -0,0 +1,8 @@
+
diff --git a/Book Files/34_Maps/Locatr/app/src/main/res/layout/fragment_locatr.xml b/Book Files/34_Maps/Locatr/app/src/main/res/layout/fragment_locatr.xml
new file mode 100644
index 0000000..563978d
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/app/src/main/res/layout/fragment_locatr.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
diff --git a/Book Files/34_Maps/Locatr/app/src/main/res/menu/fragment_locatr.xml b/Book Files/34_Maps/Locatr/app/src/main/res/menu/fragment_locatr.xml
new file mode 100644
index 0000000..1fd1370
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/app/src/main/res/menu/fragment_locatr.xml
@@ -0,0 +1,9 @@
+
+
\ No newline at end of file
diff --git a/Book Files/34_Maps/Locatr/app/src/main/res/mipmap-hdpi/ic_launcher.png b/Book Files/34_Maps/Locatr/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/Book Files/34_Maps/Locatr/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/Book Files/34_Maps/Locatr/app/src/main/res/mipmap-mdpi/ic_launcher.png b/Book Files/34_Maps/Locatr/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/Book Files/34_Maps/Locatr/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/Book Files/34_Maps/Locatr/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/Book Files/34_Maps/Locatr/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/Book Files/34_Maps/Locatr/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/Book Files/34_Maps/Locatr/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/Book Files/34_Maps/Locatr/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/Book Files/34_Maps/Locatr/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/Book Files/34_Maps/Locatr/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/Book Files/34_Maps/Locatr/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/Book Files/34_Maps/Locatr/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/Book Files/34_Maps/Locatr/app/src/main/res/values-w820dp/dimens.xml b/Book Files/34_Maps/Locatr/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/Book Files/34_Maps/Locatr/app/src/main/res/values/colors.xml b/Book Files/34_Maps/Locatr/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/Book Files/34_Maps/Locatr/app/src/main/res/values/dimens.xml b/Book Files/34_Maps/Locatr/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..663b6ba
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/app/src/main/res/values/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 16dp
+ 16dp
+ 100dp
+
diff --git a/Book Files/34_Maps/Locatr/app/src/main/res/values/strings.xml b/Book Files/34_Maps/Locatr/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..ae115c3
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/app/src/main/res/values/strings.xml
@@ -0,0 +1,6 @@
+
+ Locatr
+
+ Find an image near you
+ Map
+
diff --git a/Book Files/34_Maps/Locatr/app/src/main/res/values/styles.xml b/Book Files/34_Maps/Locatr/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/Book Files/34_Maps/Locatr/app/src/release/res/values/google_maps_api.xml b/Book Files/34_Maps/Locatr/app/src/release/res/values/google_maps_api.xml
new file mode 100644
index 0000000..1ef79e9
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/app/src/release/res/values/google_maps_api.xml
@@ -0,0 +1,20 @@
+
+
+ YOUR_KEY_HERE
+
diff --git a/Book Files/34_Maps/Locatr/app/src/test/java/com/bignerdranch/android/locatr/ExampleUnitTest.java b/Book Files/34_Maps/Locatr/app/src/test/java/com/bignerdranch/android/locatr/ExampleUnitTest.java
new file mode 100644
index 0000000..df7e954
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/app/src/test/java/com/bignerdranch/android/locatr/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.bignerdranch.android.locatr;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/Book Files/34_Maps/Locatr/build.gradle b/Book Files/34_Maps/Locatr/build.gradle
new file mode 100644
index 0000000..1ea4bd0
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/build.gradle
@@ -0,0 +1,23 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/Book Files/34_Maps/Locatr/gradle.properties b/Book Files/34_Maps/Locatr/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/Book Files/34_Maps/Locatr/gradle/wrapper/gradle-wrapper.jar b/Book Files/34_Maps/Locatr/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/Book Files/34_Maps/Locatr/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Book Files/34_Maps/Locatr/gradle/wrapper/gradle-wrapper.properties b/Book Files/34_Maps/Locatr/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a82392d
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/Book Files/34_Maps/Locatr/gradlew b/Book Files/34_Maps/Locatr/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/Book Files/34_Maps/Locatr/gradlew.bat b/Book Files/34_Maps/Locatr/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/Book Files/34_Maps/Locatr/settings.gradle b/Book Files/34_Maps/Locatr/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/Book Files/34_Maps/Locatr/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/.gitignore b/Book Files/35_MaterialDesignTopics/BeatBox/.gitignore
new file mode 100644
index 0000000..afbdab3
--- /dev/null
+++ b/Book Files/35_MaterialDesignTopics/BeatBox/.gitignore
@@ -0,0 +1,6 @@
+.gradle
+/local.properties
+/.idea/workspace.xml
+/.idea/libraries
+.DS_Store
+/build
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/.gitignore b/Book Files/35_MaterialDesignTopics/BeatBox/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/Book Files/35_MaterialDesignTopics/BeatBox/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/build.gradle b/Book Files/35_MaterialDesignTopics/BeatBox/app/build.gradle
new file mode 100644
index 0000000..5e89f58
--- /dev/null
+++ b/Book Files/35_MaterialDesignTopics/BeatBox/app/build.gradle
@@ -0,0 +1,26 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion '25.0.2'
+
+ defaultConfig {
+ applicationId "com.bignerdranch.android.beatbox"
+ minSdkVersion 15
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ compile 'com.android.support:appcompat-v7:25.3.0'
+ compile 'com.android.support:recyclerview-v7:25.3.0'
+}
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/proguard-rules.pro b/Book Files/35_MaterialDesignTopics/BeatBox/app/proguard-rules.pro
new file mode 100644
index 0000000..ccd85e1
--- /dev/null
+++ b/Book Files/35_MaterialDesignTopics/BeatBox/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /Users/bphillips/devtools/android-sdk-mac_x86/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# 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 *;
+#}
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/androidTest/java/com/bignerdranch/android/beatbox/ApplicationTest.java b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/androidTest/java/com/bignerdranch/android/beatbox/ApplicationTest.java
new file mode 100644
index 0000000..63fab8d
--- /dev/null
+++ b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/androidTest/java/com/bignerdranch/android/beatbox/ApplicationTest.java
@@ -0,0 +1,13 @@
+package com.bignerdranch.android.beatbox;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * Testing Fundamentals
+ */
+public class ApplicationTest extends ApplicationTestCase {
+ public ApplicationTest() {
+ super(Application.class);
+ }
+}
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/AndroidManifest.xml b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..0e67b8c
--- /dev/null
+++ b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/AndroidManifest.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/65_cjipie.wav b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/65_cjipie.wav
new file mode 100644
index 0000000..2b08a2f
Binary files /dev/null and b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/65_cjipie.wav differ
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/66_indios.wav b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/66_indios.wav
new file mode 100644
index 0000000..c6c0d2a
Binary files /dev/null and b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/66_indios.wav differ
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/67_indios2.wav b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/67_indios2.wav
new file mode 100644
index 0000000..c3e8008
Binary files /dev/null and b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/67_indios2.wav differ
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/68_indios3.wav b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/68_indios3.wav
new file mode 100644
index 0000000..71e08f2
Binary files /dev/null and b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/68_indios3.wav differ
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/69_ohm-loko.wav b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/69_ohm-loko.wav
new file mode 100644
index 0000000..2759e35
Binary files /dev/null and b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/69_ohm-loko.wav differ
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/70_eh.wav b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/70_eh.wav
new file mode 100644
index 0000000..903d367
Binary files /dev/null and b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/70_eh.wav differ
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/71_hruuhb.wav b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/71_hruuhb.wav
new file mode 100644
index 0000000..2987f56
Binary files /dev/null and b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/71_hruuhb.wav differ
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/72_houb.wav b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/72_houb.wav
new file mode 100644
index 0000000..d39b69d
Binary files /dev/null and b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/72_houb.wav differ
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/73_houu.wav b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/73_houu.wav
new file mode 100644
index 0000000..b59a8fa
Binary files /dev/null and b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/73_houu.wav differ
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/74_jah.wav b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/74_jah.wav
new file mode 100644
index 0000000..9d35d6c
Binary files /dev/null and b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/74_jah.wav differ
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/75_jhuee.wav b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/75_jhuee.wav
new file mode 100644
index 0000000..84adb4f
Binary files /dev/null and b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/75_jhuee.wav differ
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/76_joooaah.wav b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/76_joooaah.wav
new file mode 100644
index 0000000..6ccfcf8
Binary files /dev/null and b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/76_joooaah.wav differ
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/77_juob.wav b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/77_juob.wav
new file mode 100644
index 0000000..4d36650
Binary files /dev/null and b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/77_juob.wav differ
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/78_jueb.wav b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/78_jueb.wav
new file mode 100644
index 0000000..054d26c
Binary files /dev/null and b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/78_jueb.wav differ
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/79_long-scream.wav b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/79_long-scream.wav
new file mode 100644
index 0000000..1d36413
Binary files /dev/null and b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/79_long-scream.wav differ
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/80_oaaaahmmm.wav b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/80_oaaaahmmm.wav
new file mode 100644
index 0000000..3ce4ee2
Binary files /dev/null and b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/80_oaaaahmmm.wav differ
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/81_uehea.wav b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/81_uehea.wav
new file mode 100644
index 0000000..06c3f70
Binary files /dev/null and b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/81_uehea.wav differ
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/82_uhraa.wav b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/82_uhraa.wav
new file mode 100644
index 0000000..09cdfda
Binary files /dev/null and b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/82_uhraa.wav differ
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/83_uoh.wav b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/83_uoh.wav
new file mode 100644
index 0000000..d8bd922
Binary files /dev/null and b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/83_uoh.wav differ
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/84_uueh.wav b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/84_uueh.wav
new file mode 100644
index 0000000..e7dbfc0
Binary files /dev/null and b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/84_uueh.wav differ
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/85_jeeh.wav b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/85_jeeh.wav
new file mode 100644
index 0000000..ecc8560
Binary files /dev/null and b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/85_jeeh.wav differ
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/86_oa-h.wav b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/86_oa-h.wav
new file mode 100644
index 0000000..d806fb0
Binary files /dev/null and b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/assets/sample_sounds/86_oa-h.wav differ
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/BeatBox.java b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/BeatBox.java
new file mode 100644
index 0000000..3582a15
--- /dev/null
+++ b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/BeatBox.java
@@ -0,0 +1,78 @@
+package com.bignerdranch.android.beatbox;
+
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.AssetManager;
+import android.media.AudioManager;
+import android.media.SoundPool;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class BeatBox {
+ private static final String TAG = "BeatBox";
+
+ private static final String SOUNDS_FOLDER = "sample_sounds";
+ private static final int MAX_SOUNDS = 5;
+
+ private AssetManager mAssets;
+ private List mSounds;
+ private SoundPool mSoundPool;
+
+ public BeatBox(Context context) {
+ mAssets = context.getAssets();
+ // This old constructor is deprecated, but we need it for
+ // compatibility.
+ //noinspection deprecation
+ mSoundPool = new SoundPool(MAX_SOUNDS, AudioManager.STREAM_MUSIC, 0);
+ loadSounds();
+ }
+
+ public void play(Sound sound) {
+ Integer soundId = sound.getSoundId();
+ if (soundId == null) {
+ return;
+ }
+ mSoundPool.play(soundId, 1.0f, 1.0f, 1, 0, 1.0f);
+ }
+
+ public void release() {
+ mSoundPool.release();
+ }
+
+ public List getSounds() {
+ return mSounds;
+ }
+
+ private void loadSounds() {
+
+ String[] soundNames;
+ try {
+ soundNames = mAssets.list(SOUNDS_FOLDER);
+ Log.i(TAG, "Found " + soundNames.length + " sounds");
+ } catch (IOException ioe) {
+ Log.e(TAG, "Could not list assets", ioe);
+ return;
+ }
+
+ mSounds = new ArrayList();
+ for (String filename : soundNames) {
+ try {
+ String assetPath = SOUNDS_FOLDER + "/" + filename;
+ Sound sound = new Sound(assetPath);
+ load(sound);
+ mSounds.add(sound);
+ } catch (IOException ioe) {
+ Log.e(TAG, "Could not load sound " + filename, ioe);
+ }
+ }
+ }
+
+ private void load(Sound sound) throws IOException {
+ AssetFileDescriptor afd = mAssets.openFd(sound.getAssetPath());
+ int soundId = mSoundPool.load(afd, 1);
+ sound.setSoundId(soundId);
+ }
+}
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/BeatBoxActivity.java b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/BeatBoxActivity.java
new file mode 100644
index 0000000..cd35a39
--- /dev/null
+++ b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/BeatBoxActivity.java
@@ -0,0 +1,16 @@
+package com.bignerdranch.android.beatbox;
+
+import android.app.Fragment;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+
+
+public class BeatBoxActivity extends SingleFragmentActivity {
+
+ @Override
+ protected Fragment createFragment() {
+ return BeatBoxFragment.newInstance();
+ }
+}
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/BeatBoxFragment.java b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/BeatBoxFragment.java
new file mode 100644
index 0000000..b27c7bd
--- /dev/null
+++ b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/BeatBoxFragment.java
@@ -0,0 +1,136 @@
+package com.bignerdranch.android.beatbox;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.app.Fragment;
+import android.graphics.Point;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v7.widget.GridLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewAnimationUtils;
+import android.view.ViewGroup;
+import android.widget.Button;
+
+import java.util.List;
+
+public class BeatBoxFragment extends Fragment {
+
+ private BeatBox mBeatBox;
+ private View mRedFill;
+
+ public static BeatBoxFragment newInstance() {
+ return new BeatBoxFragment();
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setRetainInstance(true);
+
+ mBeatBox = new BeatBox(getActivity());
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.fragment_beat_box, container, false);
+
+ mRedFill = view.findViewById(R.id.red_fill);
+ mRedFill.setVisibility(View.INVISIBLE);
+
+ RecyclerView recyclerView = (RecyclerView)view
+ .findViewById(R.id.recycler_view);
+ recyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 3));
+ recyclerView.setAdapter(new SoundAdapter(mBeatBox.getSounds()));
+
+ return view;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mBeatBox.release();
+ }
+
+ private class SoundHolder extends RecyclerView.ViewHolder
+ implements View.OnClickListener {
+ private Button mButton;
+ private Sound mSound;
+
+ public SoundHolder(LayoutInflater inflater, ViewGroup parent) {
+ super(inflater.inflate(R.layout.list_item_sound, parent, false));
+
+ mButton = (Button)itemView.findViewById(R.id.button);
+ mButton.setOnClickListener(this);
+ }
+
+ public void bindSound(Sound sound) {
+ mSound = sound;
+ mButton.setText(mSound.getName());
+ }
+
+ @Override
+ public void onClick(View v) {
+ int[] clickCoords = new int[2];
+ v.getLocationOnScreen(clickCoords);
+
+ clickCoords[0] += (v.getWidth() / 2);
+ clickCoords[1] += (v.getHeight() / 2);
+
+ performRevealAnimation(mRedFill, clickCoords[0], clickCoords[1]);
+ mBeatBox.play(mSound);
+ }
+ }
+
+ private void performRevealAnimation(final View view, int screenCenterX, int screenCenterY) {
+ int[] animatingViewCoords = new int[2];
+ view.getLocationOnScreen(animatingViewCoords);
+ int centerX = screenCenterX - animatingViewCoords[0];
+ int centerY = screenCenterY - animatingViewCoords[1];
+
+ Point size = new Point();
+ getActivity().getWindowManager().getDefaultDisplay().getSize(size);
+ int maxRadius = size.y;
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ view.setVisibility(View.VISIBLE);
+ Animator animator = ViewAnimationUtils.createCircularReveal(view, centerX, centerY, 0, maxRadius);
+ animator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ view.setVisibility(View.INVISIBLE);
+ }
+ });
+ animator.start();
+ }
+ }
+
+ private class SoundAdapter extends RecyclerView.Adapter {
+ private List mSounds;
+
+ public SoundAdapter(List sounds) {
+ mSounds = sounds;
+ }
+
+ @Override
+ public SoundHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
+ LayoutInflater inflater = LayoutInflater.from(getActivity());
+ return new SoundHolder(inflater, viewGroup);
+ }
+
+ @Override
+ public void onBindViewHolder(SoundHolder soundHolder, int i) {
+ Sound sound = mSounds.get(i);
+ soundHolder.bindSound(sound);
+ }
+
+ @Override
+ public int getItemCount() {
+ return mSounds.size();
+ }
+ }
+}
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/SingleFragmentActivity.java b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/SingleFragmentActivity.java
new file mode 100644
index 0000000..5770ef5
--- /dev/null
+++ b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/SingleFragmentActivity.java
@@ -0,0 +1,29 @@
+package com.bignerdranch.android.beatbox;
+
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+
+public abstract class SingleFragmentActivity extends AppCompatActivity {
+ protected abstract Fragment createFragment();
+
+ protected int getLayoutResId() {
+ return R.layout.activity_fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(getLayoutResId());
+ FragmentManager manager = getFragmentManager();
+ Fragment fragment = manager.findFragmentById(R.id.fragmentContainer);
+
+ if (fragment == null) {
+ fragment = createFragment();
+ manager.beginTransaction()
+ .add(R.id.fragmentContainer, fragment)
+ .commit();
+ }
+ }
+}
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/Sound.java b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/Sound.java
new file mode 100644
index 0000000..531733e
--- /dev/null
+++ b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/java/com/bignerdranch/android/beatbox/Sound.java
@@ -0,0 +1,30 @@
+package com.bignerdranch.android.beatbox;
+
+public class Sound {
+ private String mAssetPath;
+ private String mName;
+ private Integer mSoundId;
+
+ public Sound(String assetPath) {
+ mAssetPath = assetPath;
+ String[] components = assetPath.split("/");
+ String filename = components[components.length - 1];
+ mName = filename.replace(".wav", "");
+ }
+
+ public String getAssetPath() {
+ return mAssetPath;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public Integer getSoundId() {
+ return mSoundId;
+ }
+
+ public void setSoundId(Integer soundId) {
+ mSoundId = soundId;
+ }
+}
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/res/drawable-xxhdpi/ic_button_beat_box_default.png b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/res/drawable-xxhdpi/ic_button_beat_box_default.png
new file mode 100644
index 0000000..ce5ecee
Binary files /dev/null and b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/res/drawable-xxhdpi/ic_button_beat_box_default.png differ
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/res/drawable-xxhdpi/ic_button_beat_box_pressed.png b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/res/drawable-xxhdpi/ic_button_beat_box_pressed.png
new file mode 100644
index 0000000..bbbacfb
Binary files /dev/null and b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/res/drawable-xxhdpi/ic_button_beat_box_pressed.png differ
diff --git a/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/res/drawable/button_beat_box.xml b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/res/drawable/button_beat_box.xml
new file mode 100644
index 0000000..d57dc45
--- /dev/null
+++ b/Book Files/35_MaterialDesignTopics/BeatBox/app/src/main/res/drawable/button_beat_box.xml
@@ -0,0 +1,6 @@
+
+