This commit is contained in:
danijoo
2015-11-05 22:34:08 +01:00
commit 3821c3e494
48 changed files with 1306 additions and 0 deletions

9
.gitignore vendored Normal file
View File

@@ -0,0 +1,9 @@
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
*.iml
.idea/

144
README.md Normal file
View File

@@ -0,0 +1,144 @@
# UdacityProject4: Build-it-bigger
A demo app showing how multiple modules work together.
# Origininal Readme
# Gradle for Android and Java Final Project
In this project, you will create an app with multiple flavors that uses
multiple libraries and Google Could Endpoints. The finished app will consist
of four modules. A Java library that provides jokes, a Google Could Endpoints
(GCE) project that serves those jokes, an Android Library containing an
activity for displaying jokes, and an Android app that fetches jokes from the
GCE module and passes them to the Android Library for display.
## Why this Project
As Android projects grow in complexity, it becomes necessary to customize the
behavior of the Gradle build tool, allowing automation of repetitive tasks.
Particularly, factoring functionality into libraries and creating product
flavors allow for much bigger projects with minimal added complexity.
##What Will I Learn?
You will learn the role of Gradle in building Android Apps and how to use Gradle to manage apps of increasing complexity. You'll learn to:
* Add free and paid flavors to an app, and set up your build to share code between them
* Factor reusable functionality into a Java library
* Factor reusable Android functionality into an Android library
* Configure a multi project build to compile your libraries and app
* Use the Gradle App Engine plugin to deploy a backend
* Configure an integration test suite that runs against the local App Engine development server
##How Do I Complete this Project?
### Step 0: Starting Point
This is the starting point for the final project, which is provided to you in the [course repository](https://github.com/udacity/ud867/tree/master/FinalProject).
It contains an activity with a banner ad and a button that purports to tell a
joke, but actually just complains. The banner ad was set up following the
instructions here:
https://developers.google.com/mobile-ads-sdk/docs/admob/android/quick-start
You may need to download the Google Repository from the Extras section of the
Android SDK Manager.
When you can build an deploy this starter code to an emulator, you're ready to
move on.
### Step 1: Create a Java library
Your first task is to create a Java library that provides jokes. Create a new
Gradle Java project either using the Android Studio wizard, or by hand. Then
introduce a project dependency between your app and the new Java Library. If
you need review, check out demo 4.01 from the course code.
Make the button display a toast showing a joke retrieved from your Java joke
telling library.
### Step 2: Create an Android Library
Create an Android Library containing an Activity that will display a joke
passed to it as an intent extra. Wire up project dependencies so that the
button can now pass the joke from the Java Library to the Android Library.
For review on how to create an Android library, check out demo 4.03. For a
refresher on intent extras, check out;
http://developer.android.com/guide/components/intents-filters.html
### Step 3: Create GCE Module
This next task will be pretty tricky. Instead of pulling jokes directly from
our Java library, we'll set up a Google Cloud Endpoints development server,
and pull our jokes from there. Follow the instructions in the following
tutorial to add a Google Could Endpoints module to your project:
https://github.com/GoogleCloudPlatform/gradle-appengine-templates/tree/master/HelloEndpoints
Introduce a project dependency between your Java library and your GCE module,
and modify the GCE starter code to pull jokes from your Java library. Create
an Async task to retrieve jokes. Make the button kick off a task to retrieve a
joke, then launch the activity from your Android Library to display it.
### Step 4: Add Functional Tests
Add code to test that your Async task successfully retrieves a non-empty
string. For a refresher on setting up Android tests, check out demo 4.09.
### Step 5: Add a Paid Flavor
Add free and paid product flavors to your app. Remove the ad (and any
dependencies you can) from the paid flavor.
## Optional Tasks
To exceed expectations, do the following:
### Add Interstitial Ad
Follow these instructions to add an interstitial ad to the free version.
Display the add after the user hits the button, but before the joke is shown.
https://developers.google.com/mobile-ads-sdk/docs/admob/android/interstitial
### Add Loading Indicator
Add a loading indicator that is shown while the joke is being retrieved and
disappears when the joke is ready. The following tutorial is a good place to
start:
http://www.tutorialspoint.com/android/android_loading_spinner.htm
### Configure Test Task
To tie it all together, create a Gradle task that:
1. Launches the GCE local development server
2. Runs all tests
3. Shuts the server down again
# Rubric
### Required Components
* Project contains a Java library for supplying jokes
* Project contains an Android library with an activity that displays jokes passed to it as intent extras.
* Project contains a Google Cloud Endpoints module that supplies jokes from the Java library. Project loads jokes from GCE module via an async task.
* Project contains connected tests to verify that the async task is indeed loading jokes.
* Project contains paid/free flavors. The paid flavor has no ads, and no unnecessary dependencies.
### Required Behavior
* App retrieves jokes from Google Cloud Endpoints module and displays them via an Activity from the Android Library.
### Optional Components
To receive "exceeds specifications", your app must fully implement all of the following items.
* The free app variant displays interstitial ads between the main activity and the joke-displaying activity.
* The app displays a loading indicator while the joke is being fetched from the server.
* The root build.gradle file contains a task that will start up the GCE development server, run all Android tests, then shutdown the development server.

1
androidjokepresenter/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

View File

@@ -0,0 +1,23 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
minSdkVersion 16
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile 'com.android.support:appcompat-v7:23.1.0'
}

17
androidjokepresenter/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in C:\android-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 *;
#}

View File

@@ -0,0 +1,13 @@
package net.headlezz.androidjokepresenter;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
}

View File

@@ -0,0 +1,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.headlezz.androidjokepresenter">
<application android:allowBackup="true" android:label="@string/app_name"
android:supportsRtl="true">
</application>
</manifest>

View File

@@ -0,0 +1,69 @@
package net.headlezz.androidjokepresenter;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.ShareActionProvider;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
public class JokePresenterActivity extends AppCompatActivity {
public static final String BUNDLE_ARG_JOKE = "joke";
TextView tvJokes;
String joke;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.joke_presenster_activity);
tvJokes = (TextView) findViewById(R.id.tvJokes);
if (savedInstanceState != null)
setJokeFromBundle(savedInstanceState);
else
setJokeFromBundle(getIntent().getExtras());
}
/**
* sets the joke from the bundle to the activity
*
* @param b undle
*/
private void setJokeFromBundle(Bundle b) {
if (b.containsKey(BUNDLE_ARG_JOKE)) {
joke = b.getString(BUNDLE_ARG_JOKE);
tvJokes.setText(joke);
} else
throw new RuntimeException(JokePresenterActivity.class.getSimpleName() + " started without a joke!");
}
/**
* Updated the share intent of the share action provider
*/
private void updateShareIntent(ShareActionProvider provider) {
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.putExtra(Intent.EXTRA_TEXT, joke);
shareIntent.setType("text/plain");
shareIntent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.share_intent_title));
provider.setShareIntent(shareIntent);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.joke_presenter_activity_menu, menu);
MenuItem item = menu.findItem(R.id.menu_item_share);
ShareActionProvider mShareProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(item);
updateShareIntent(mShareProvider);
return super.onCreateOptionsMenu(menu);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putString(BUNDLE_ARG_JOKE, joke);
super.onSaveInstanceState(outState);
}
}

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textAppearance="?android:attr/textAppearanceMedium"
tools:text="A joke"
android:id="@+id/tvJokes"
android:layout_gravity="center_horizontal" />
</LinearLayout>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_item_share"
app:showAsAction="ifRoom"
android:title="@string/share"
app:actionProviderClass=
"android.support.v7.widget.ShareActionProvider" />
</menu>

View File

@@ -0,0 +1,5 @@
<resources>
<string name="app_name">androidjokepresenter</string>
<string name="share">Share</string>
<string name="share_intent_title">I laughed so hard!</string>
</resources>

View File

@@ -0,0 +1,15 @@
package net.headlezz.androidjokepresenter;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* To work on unit tests, switch the Test Artifact in the Build Variants view.
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() throws Exception {
assertEquals(4, 2 + 2);
}
}

1
app/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

30
app/build.gradle Normal file
View File

@@ -0,0 +1,30 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "22.0.1"
defaultConfig {
applicationId "com.udacity.gradle.builditbigger"
minSdkVersion 16
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
// Added for AdMob
compile project(':androidjokepresenter')
compile 'com.android.support:appcompat-v7:23.1.0'
compile 'com.google.android.gms:play-services-ads:8.1.0'
compile project(path: ':jokesbackend', configuration: 'android-endpoints')
compile 'com.android.support:design:23.1.0'
}

17
app/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/silver/Development/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 *;
#}

View File

@@ -0,0 +1,13 @@
package com.udacity.gradle.builditbigger;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
}

View File

@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.udacity.gradle.builditbigger" >
<!-- Include required permissions for Google Mobile Ads to run -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<!-- This meta-data tag is required to use Google Play Services. -->
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="net.headlezz.androidjokepresenter.JokePresenterActivity"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".MainActivity" />
</activity>
<!-- Include the AdActivity configChanges and theme. -->
<activity
android:name="com.google.android.gms.ads.AdActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"
android:theme="@android:style/Theme.Translucent" />
</application>
</manifest>

View File

@@ -0,0 +1,60 @@
package com.udacity.gradle.builditbigger;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.extensions.android.json.AndroidJsonFactory;
import com.google.api.client.googleapis.services.AbstractGoogleClientRequest;
import com.google.api.client.googleapis.services.GoogleClientRequestInitializer;
import net.headlezz.jokesbackend.myApi.MyApi;
import net.headlezz.jokesbackend.myApi.model.Joke;
import java.io.IOException;
public class JokeLoaderTask extends AsyncTask<Void, Void, Joke> {
static final String ROOT_URL = "http://10.0.3.2:8080/_ah/api/";
MyApi mApi;
JokeLoaderCallback mCallback;
interface JokeLoaderCallback {
void onJokeLoaded(Joke joke);
void onError();
}
public JokeLoaderTask(@NonNull JokeLoaderCallback cb) {
mCallback = cb;
mApi = new MyApi.Builder(AndroidHttp.newCompatibleTransport(), new AndroidJsonFactory(), null)
.setRootUrl(ROOT_URL)
.setGoogleClientRequestInitializer(new GoogleClientRequestInitializer() {
@Override
public void initialize(AbstractGoogleClientRequest<?> abstractGoogleClientRequest) throws IOException {
abstractGoogleClientRequest.setDisableGZipContent(true);
}
}).build();
}
@Override
protected Joke doInBackground(Void... params) {
try {
return mApi.tellJoke().execute();
} catch (IOException e) {
return null;
}
}
@Override
protected void onPostExecute(Joke joke) {
if(isCancelled())
return;
if(joke != null) {
mCallback.onJokeLoaded(joke);
} else {
mCallback.onError();
}
}
}

View File

@@ -0,0 +1,40 @@
package com.udacity.gradle.builditbigger;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}

View File

@@ -0,0 +1,76 @@
package com.udacity.gradle.builditbigger;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.AdView;
import net.headlezz.androidjokepresenter.JokePresenterActivity;
import net.headlezz.jokesbackend.myApi.model.Joke;
/**
* A placeholder fragment containing a simple view.
*/
public class MainActivityFragment extends Fragment implements View.OnClickListener, JokeLoaderTask.JokeLoaderCallback {
JokeLoaderTask mJokeLoaderTask;
public MainActivityFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_main, container, false);
initAds((AdView) root.findViewById(R.id.adView));
root.findViewById(R.id.btShowJoke).setOnClickListener(this);
return root;
}
private void initAds(AdView adView) {
AdRequest adRequest = new AdRequest.Builder()
.addTestDevice(AdRequest.DEVICE_ID_EMULATOR)
.build();
adView.loadAd(adRequest);
}
@Override
public void onClick(View v) {
downloadNewJoke();
}
private void downloadNewJoke() {
mJokeLoaderTask = new JokeLoaderTask(this);
mJokeLoaderTask.execute();
}
@Override
public void onJokeLoaded(Joke joke) {
Intent i = new Intent(getContext(), JokePresenterActivity.class);
i.putExtra(JokePresenterActivity.BUNDLE_ARG_JOKE, joke.getJoke());
startActivity(i);
}
@Override
public void onError() {
if (getView() != null)
Snackbar.make(getView(), R.string.joke_download_error, Snackbar.LENGTH_LONG)
.setAction(R.string.retry, this)
.show();
}
@Override
public void onStop() {
if(mJokeLoaderTask != null && !mJokeLoaderTask.isCancelled())
mJokeLoaderTask.cancel(true);
super.onStop();
}
}

View File

@@ -0,0 +1,5 @@
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:id="@+id/fragment"
android:name="com.udacity.gradle.builditbigger.MainActivityFragment"
tools:layout="@layout/fragment_main" android:layout_width="match_parent"
android:layout_height="match_parent" />

View File

@@ -0,0 +1,36 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:ads="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivityFragment">
<TextView android:text="@string/instructions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/instructions_text_view"
android:layout_above="@+id/btShowJoke"
android:layout_centerHorizontal="true" />
<Button
android:id="@+id/btShowJoke"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button_text"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
<com.google.android.gms.ads.AdView
android:id="@+id/adView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
ads:adSize="BANNER"
ads:adUnitId="@string/banner_ad_unit_id">
</com.google.android.gms.ads.AdView>
</RelativeLayout>

View File

@@ -0,0 +1,6 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity">
<item android:id="@+id/action_settings" android:title="@string/action_settings"
android:orderInCategory="100" app:showAsAction="never" />
</menu>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

@@ -0,0 +1,6 @@
<resources>
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
(such as screen margins) for screens with more than 820dp of available width. This
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
<dimen name="activity_horizontal_margin">64dp</dimen>
</resources>

View File

@@ -0,0 +1,5 @@
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
</resources>

View File

@@ -0,0 +1,9 @@
<resources>
<string name="app_name">Build it Bigger</string>
<string name="instructions">Press the button for a delicious joke!</string>
<string name="button_text">Tell Joke</string>
<string name="action_settings">Settings</string>
<string name="banner_ad_unit_id">ca-app-pub-3940256099942544/6300978111</string>
<string name="joke_download_error">Failed to load joke</string>
<string name="retry">Retry</string>
</resources>

View File

@@ -0,0 +1,8 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
</style>
</resources>

19
build.gradle Normal file
View File

@@ -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:1.2.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}

18
gradle.properties Normal file
View File

@@ -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

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

View File

@@ -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-2.2.1-all.zip

164
gradlew vendored Normal file
View File

@@ -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 "$@"

90
gradlew.bat vendored Normal file
View File

@@ -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

1
jokeprovider/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/build

View File

@@ -0,0 +1,8 @@
apply plugin: 'java'
sourceCompatibility = 1.7
targetCompatibility = 1.7
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}

View File

@@ -0,0 +1,33 @@
package net.headlezz.jokeprovider;
import java.util.Random;
/**
* Provder class for jokes
*/
public class JokeProvider {
Random random = new Random();
/**
* jokes taken from http://www.devtopics.com/best-programming-jokes/
*/
String[] jokes = new String[] {
"Two bytes meet. The first byte asks, “Are you ill?”\nThe second byte replies, “No, just feeling a bit off.”",
"Eight bytes walk into a bar. The bartender asks, “Can I get you anything?”\n\n“Yeah,” reply the bytes. “Make us a double.”",
"Q. How did the programmer die in the shower?\nA. He read the shampoo bottle instructions: Lather. Rinse. Repeat.",
"How many programmers does it take to change a light bulb?\nNone Its a hardware problem",
"Why do programmers always mix up Halloween and Christmas?\nBecause Oct 31 equals Dec 25.",
"There are only 10 kinds of people in this world: those who know binary and those who dont.",
"A programmer walks to the butcher shop and buys a kilo of meat. An hour later he comes back upset that the butcher shortchanged him by 24 grams.",
"Programming is 10% science, 20% ingenuity, and 70% getting the ingenuity to work with the science.",
"Programming is like sex: One mistake and you have to support it for the rest of your life.",
"All programmers are playwrights, and all computers are lousy actors."
};
public String getRandomJoke() {
int randomIndex = random.nextInt(jokes.length);
return jokes[randomIndex];
}
}

41
jokesbackend/build.gradle Normal file
View File

@@ -0,0 +1,41 @@
// If you would like more information on the gradle-appengine-plugin please refer to the github page
// https://github.com/GoogleCloudPlatform/gradle-appengine-plugin
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.google.appengine:gradle-appengine-plugin:1.9.18'
}
}
repositories {
jcenter();
}
apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'appengine'
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
dependencies {
appengineSdk 'com.google.appengine:appengine-java-sdk:1.9.18'
compile 'com.google.appengine:appengine-endpoints:1.9.18'
compile 'com.google.appengine:appengine-endpoints-deps:1.9.18'
compile 'javax.servlet:servlet-api:2.5'
compile project(':jokeprovider')
}
appengine {
downloadSdk = true
appcfg {
oauth2 = true
}
endpoints {
getClientLibsOnBuild = true
getDiscoveryDocsOnBuild = true
}
}

View File

@@ -0,0 +1,18 @@
package net.headlezz.jokesbackend;
/**
* Wrapper arround jokes
*/
public class Joke {
String mJoke;
public Joke(String joke) {
mJoke = joke;
}
public String getJoke() {
return mJoke;
}
}

View File

@@ -0,0 +1,39 @@
/*
For step-by-step instructions on connecting your Android application to this backend module,
see "App Engine Java Endpoints Module" template documentation at
https://github.com/GoogleCloudPlatform/gradle-appengine-templates/tree/master/HelloEndpoints
*/
package net.headlezz.jokesbackend;
import com.google.api.server.spi.config.Api;
import com.google.api.server.spi.config.ApiMethod;
import com.google.api.server.spi.config.ApiNamespace;
import net.headlezz.jokeprovider.JokeProvider;
/**
* An endpoint class we are exposing
*/
@Api(
name = "myApi",
version = "v1",
namespace = @ApiNamespace(
ownerDomain = "jokesbackend.headlezz.net",
ownerName = "jokesbackend.headlezz.net",
packagePath = ""
)
)
public class MyEndpoint {
JokeProvider jp = new JokeProvider();
/**
* A simple endpoint method that takes a name and says Hi back
*/
@ApiMethod(name = "tellJoke")
public Joke tellJoke () {
return new Joke(jp.getRandomJoke());
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
<application>myApplicationId</application>
<version>1</version>
<threadsafe>true</threadsafe>
<system-properties>
<property name="java.util.logging.config.file" value="WEB-INF/logging.properties" />
</system-properties>
</appengine-web-app>

View File

@@ -0,0 +1,13 @@
# A default java.util.logging configuration.
# (All App Engine logging is through java.util.logging by default).
#
# To use this configuration, copy it into your application's WEB-INF
# folder and add the following to your appengine-web.xml:
#
# <system-properties>
# <property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
# </system-properties>
#
# Set the default logging level for all loggers to WARNING
.level = WARNING

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5">
<servlet>
<servlet-name>SystemServiceServlet</servlet-name>
<servlet-class>com.google.api.server.spi.SystemServiceServlet</servlet-class>
<init-param>
<param-name>services</param-name>
<param-value>net.headlezz.jokesbackend.MyEndpoint</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>SystemServiceServlet</servlet-name>
<url-pattern>/_ah/spi/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>

View File

@@ -0,0 +1,140 @@
<!DOCTYPE html>
<html>
<head>
<title>Hello, Endpoints!</title>
<link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
<link rel="stylesheet"
href="//netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap-theme.min.css">
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script src="//netdna.bootstrapcdn.com/bootstrap/3.1.1/js/bootstrap.min.js"></script>
</head>
<body role="document" style="padding-top: 70px;">
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="#">Hello, Endpoints!</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Documentation <b
class="caret"></b></a>
<ul class="dropdown-menu">
<li><a href="https://developers.google.com/appengine/docs/java/">Google App
Engine</a></li>
<li><a href="https://developers.google.com/appengine/docs/java/endpoints/">Google
Cloud Endpoints</a></li>
<li>
<a href="https://github.com/GoogleCloudPlatform/gradle-appengine-templates/tree/master/HelloEndpoints">Connecting
your Android application to this backend</a></li>
</ul>
</li>
<li><a href="/_ah/api/explorer">Google Cloud Endpoints API Explorer</a></li>
<li><a href="https://console.developers.google.com">Google Developers Console</a>
</li>
</ul>
</div>
</div>
</div>
<div class="container theme-showcase" role="main">
<!--
Output from Endpoints API call.
-->
<div class="alert alert-success" style="visibility: collapse;" id="outputAlert"></div>
<!--
A form that takes a text value and submits it to the Endpoint,
access to the Endpoint is enabled once the client is loaded below.
-->
<div class="jumbotron">
<div class="row">
<div class="col-lg-12">
<h1>Hello, Endpoints!</h1>
<p>Enter your name and press the button below to call your Google Cloud Endpoints
API.</p>
<form>
<div class="input-group">
<input type="text" class="form-control input-lg" placeholder="Name"
id="nameInput"/>
<span class="input-group-btn">
<button class="btn btn-default btn-primary btn-group btn-lg"
type="submit" id="helloButton">Say "Hello" &raquo;</button>
</span>
</div>
</form>
<br/>
<p>If you need step-by-step instructions for connecting your Android application to
this backend module, see <a
href="https://github.com/GoogleCloudPlatform/gradle-appengine-templates/tree/master/HelloEndpoints">"App
Engine Java Endpoints Module" template documentation</a>.</p>
<p>
<small>
For more information about Google App Engine for Java, check out the <a
href="https://developers.google.com/appengine/docs/java/">App Engine
documentation</a>.<br/>
To learn more about Google Cloud Endpoints, see <a
href="https://developers.google.com/appengine/docs/java/endpoints/">Cloud
Endpoints documentation</a>.<br/>
If you'd like to access your generated Google Cloud Endpoints APIs directly,
see the <a href="/_ah/api/explorer">Cloud Endpoints API Explorer</a>.
</small>
</p>
</div>
</div>
</div>
</div>
<script type="text/javascript">
// A function that attaches a "Say Hello" button click handler
function enableClick() {
document.getElementById('helloButton').onclick = function() {
var name = document.getElementById('nameInput').value;
gapi.client.myApi.sayHi({'name': name}).execute(
function(response) {
var outputAlertDiv = document.getElementById('outputAlert');
outputAlertDiv.style.visibility = 'visible';
if (!response.error) {
outputAlertDiv.className = 'alert alert-success';
outputAlertDiv.innerHTML = '<h2>' + response.result.data + '</h2>';
}
else if (response.error) {
outputAlertDiv.className = 'alert alert-danger';
outputAlertDiv.innerHTML = '<b>Error Code: </b>' + response.error.code + ' [' + response.error.message + ']';
}
}
);
return false;
}
}
// This is called initially
function init() {
var apiName = 'myApi';
var apiVersion = 'v1';
var apiRoot = 'https://' + window.location.host + '/_ah/api';
if (window.location.hostname == 'localhost'
|| window.location.hostname == '127.0.0.1'
|| ((window.location.port != "") && (window.location.port > 1023))) {
// We're probably running against the DevAppServer
apiRoot = 'http://' + window.location.host + '/_ah/api';
}
var callback = function() {
enableClick();
}
gapi.client.load(apiName, apiVersion, callback, apiRoot);
}
</script>
<!--
Load the Google APIs Client Library for JavaScript
More info here : https://developers.google.com/api-client-library/javascript/reference/referencedocs
-->
<script src="https://apis.google.com/js/client.js?onload=init"></script>
</body>
</html>

1
settings.gradle Normal file
View File

@@ -0,0 +1 @@
include ':jokeprovider', ':app', ':androidjokepresenter', ':jokesbackend'