├── .gitignore ├── CODEOWNERS ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── android │ │ └── example │ │ └── com │ │ └── squawker │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── ic_launcher-web.png │ ├── java │ │ └── android │ │ │ └── example │ │ │ └── com │ │ │ └── squawker │ │ │ ├── MainActivity.java │ │ │ ├── SquawkAdapter.java │ │ │ ├── fcm │ │ │ ├── SquawkFirebaseInstanceIdService.java │ │ │ └── SquawkFirebaseMessageService.java │ │ │ ├── following │ │ │ ├── FollowingPreferenceActivity.java │ │ │ └── FollowingPreferenceFragment.java │ │ │ └── provider │ │ │ ├── SquawkContract.java │ │ │ ├── SquawkDatabase.java │ │ │ └── SquawkProvider.java │ └── res │ │ ├── drawable-hdpi-v11 │ │ └── ic_duck.png │ │ ├── drawable-hdpi-v9 │ │ └── ic_duck.png │ │ ├── drawable-hdpi │ │ ├── ic_duck.png │ │ └── ic_follow.png │ │ ├── drawable-mdpi-v11 │ │ └── ic_duck.png │ │ ├── drawable-mdpi-v9 │ │ └── ic_duck.png │ │ ├── drawable-mdpi │ │ ├── ic_duck.png │ │ └── ic_follow.png │ │ ├── drawable-xhdpi-v11 │ │ └── ic_duck.png │ │ ├── drawable-xhdpi-v9 │ │ └── ic_duck.png │ │ ├── drawable-xhdpi │ │ ├── ic_duck.png │ │ └── ic_follow.png │ │ ├── drawable-xxhdpi-v11 │ │ └── ic_duck.png │ │ ├── drawable-xxhdpi-v9 │ │ └── ic_duck.png │ │ ├── drawable-xxhdpi │ │ ├── ic_duck.png │ │ └── ic_follow.png │ │ ├── drawable │ │ ├── asser.png │ │ ├── cezanne.png │ │ ├── ic_person_black_48dp.xml │ │ ├── jlin.png │ │ ├── lyla.png │ │ ├── nikita.png │ │ └── test.png │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── activity_settings.xml │ │ └── item_squawk_list.xml │ │ ├── menu │ │ └── main_menu.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ ├── values │ │ ├── bool.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ └── following_squawker.xml │ └── test │ └── java │ └── android │ └── example │ └── com │ └── squawker │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | .idea/ 3 | *.iml 4 | build/ 5 | /local.properties 6 | .DS_Store 7 | /captures 8 | google-services.json 9 | 10 | 11 | # Created by https://www.gitignore.io/api/intellij,android,java 12 | 13 | ### Intellij ### 14 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 15 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 16 | 17 | # User-specific stuff: 18 | .idea/workspace.xml 19 | .idea/tasks.xml 20 | 21 | # Sensitive or high-churn files: 22 | .idea/dataSources/ 23 | .idea/dataSources.ids 24 | .idea/dataSources.xml 25 | .idea/dataSources.local.xml 26 | .idea/sqlDataSources.xml 27 | .idea/dynamic.xml 28 | .idea/uiDesigner.xml 29 | 30 | # Gradle: 31 | .idea/gradle.xml 32 | .idea/libraries 33 | 34 | # Mongo Explorer plugin: 35 | .idea/mongoSettings.xml 36 | 37 | ## File-based project format: 38 | *.iws 39 | 40 | ## Plugin-specific files: 41 | 42 | # IntelliJ 43 | /out/ 44 | 45 | # mpeltonen/sbt-idea plugin 46 | .idea_modules/ 47 | 48 | # JIRA plugin 49 | atlassian-ide-plugin.xml 50 | 51 | # Crashlytics plugin (for Android Studio and IntelliJ) 52 | com_crashlytics_export_strings.xml 53 | crashlytics.properties 54 | crashlytics-build.properties 55 | fabric.properties 56 | 57 | ### Intellij Patch ### 58 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 59 | 60 | # *.iml 61 | # modules.xml 62 | # .idea/misc.xml 63 | # *.ipr 64 | 65 | 66 | ### Android ### 67 | # Built application files 68 | *.apk 69 | *.ap_ 70 | 71 | # Files for the ART/Dalvik VM 72 | *.dex 73 | 74 | # Java class files 75 | *.class 76 | 77 | # Generated files 78 | bin/ 79 | gen/ 80 | out/ 81 | 82 | # Gradle files 83 | .gradle/ 84 | build/ 85 | 86 | # Local configuration file (sdk path, etc) 87 | local.properties 88 | 89 | # Proguard folder generated by Eclipse 90 | proguard/ 91 | 92 | # Log Files 93 | *.log 94 | 95 | # Android Studio Navigation editor temp files 96 | .navigation/ 97 | 98 | # Android Studio captures folder 99 | captures/ 100 | 101 | # Intellij 102 | *.iml 103 | 104 | # Keystore files 105 | *.jks 106 | 107 | # External native build folder generated in Android Studio 2.2 and later 108 | .externalNativeBuild 109 | 110 | ### Android Patch ### 111 | gen-external-apklibs 112 | 113 | 114 | ### Java ### 115 | 116 | # BlueJ files 117 | *.ctxt 118 | 119 | # Mobile Tools for Java (J2ME) 120 | .mtj.tmp/ 121 | 122 | # Package Files # 123 | *.jar 124 | *.war 125 | *.ear 126 | 127 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 128 | hs_err_pid* 129 | 130 | # End of https://www.gitignore.io/api/intellij,android,java -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @udacity/active-public-content -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Squawker Code 2 | 3 | This is a exercise repository for the Squawker example app which is part of Udacity's Advanced Android course. The Squawker example app uses Firebase Cloud Messenger to receive Twitter-like messages, sent from [this server](https://squawkerfcmserver.udacity.com/), in real time. 4 | 5 | This is the toy app for the FCM lesson of the [Advanced Android App Development course on Udacity](https://www.udacity.com/course/advanced-android-app-development--ud855). 6 | 7 | ## How to use this repo while taking the course 8 | 9 | Each code repository in this class has a chain of commits that looks like this: 10 | 11 | ![listofcommits](https://d17h27t6h515a5.cloudfront.net/topher/2017/March/58befe2e_listofcommits/listofcommits.png) 12 | 13 | These commits show every step you'll take to create the app. They include **Exercise** commits and **Solution** commits. 14 | 15 | Exercise commits contain instructions for completing the exercise, while solution commits show the completed exercise. You can tell what a commit is by looking at its commit message. 16 | 17 | For example, **TFCM.01-Exercise-AddGradleDependencies** is the first code step in the Firebase Cloud Messaging (FCM) lesson. This is the exercise commit, and the exercise is called Add Gradle Dependencies. 18 | 19 | Each commit also has a **branch** associated with it of the same name as the commit message, seen below: 20 | 21 | ![branches](https://d17h27t6h515a5.cloudfront.net/topher/2017/April/590390fe_branches-ud855/branches-ud855.png 22 | ) 23 | Access all branches from this tab 24 | 25 | ![listofbranches](https://d17h27t6h515a5.cloudfront.net/topher/2017/March/58befe76_listofbranches/listofbranches.png 26 | ) 27 | 28 | 29 | ![branchesdropdown](https://d17h27t6h515a5.cloudfront.net/topher/2017/April/590391a3_branches-dropdown-ud855/branches-dropdown-ud855.png 30 | ) 31 | 32 | 33 | The branches are also accessible from the drop-down in the "Code" tab 34 | 35 | 36 | ## Working with the Course Code 37 | 38 | Here are the basic steps for working with and completing exercises in the repo. This information is linked whenever you start a new exercise project, so don't feel you need to memorize all of this! In fact, skim it now, make sure that you know generally how to do the different tasks, and then come back when you start your first exercise. 39 | 40 | The basic steps are: 41 | 42 | 1. Clone the repo 43 | 2. Checkout the exercise branch 44 | 3. Find and complete the TODOs 45 | 4. Optionally commit your code changes 46 | 5. Compare with the solution 47 | 48 | 49 | **Step 1: Clone the repo** 50 | 51 | As you go through the course, you'll be instructed to clone the different exercise repositories, so you don't need to set these up now. You can clone a repository from github in a folder of your choice with the command: 52 | 53 | ```bash 54 | git clone https://github.com/udacity/REPOSITORY_NAME.git 55 | ``` 56 | 57 | **Step 2: Checkout the exercise branch** 58 | 59 | As you do different exercises in the code, you'll be told which exercise you're on, as seen below: 60 | ![exerciseexample](https://d17h27t6h515a5.cloudfront.net/topher/2017/March/58bf0087_exerciseexample/exerciseexample.png 61 | ) 62 | 63 | To complete an exercise, you'll want to check out the branch associated with that exercise. For the exercise above, the command to check out that branch would be: 64 | 65 | ```bash 66 | git checkout TFCM.01-Exercise-AddGradleDependencies 67 | ``` 68 | 69 | **Step 3: Find and complete the TODOs** 70 | 71 | This branch should always have **Exercise** in the title. Once you've checked out the branch, you'll have the code in the exact state you need. You'll even have TODOs, which are special comments that tell you all the steps you need to complete the exercise. You can easily navigate to all the TODOs using Android Studio's TODO tool. To open the TODO tool, click the button at the bottom of the screen that says TODO. This will display a list of all comments with TODO in the project. 72 | 73 | We've numbered the TODO steps so you can do them in order: 74 | ![todos](https://d17h27t6h515a5.cloudfront.net/topher/2017/March/58bf00e7_todos/todos.png 75 | ) 76 | 77 | **Step 4: Optionally commit your code changes** 78 | 79 | After You've completed the TODOs, you can optionally commit your changes. This will allow you to see the code you wrote whenever you return to the branch. The following git code will add and save **all** your changes. 80 | 81 | ```bash 82 | git add . 83 | git commit -m "Your commit message" 84 | ``` 85 | 86 | **Step 5: Compare with the solution** 87 | 88 | Most exercises will have a list of steps for you to check off in the classroom. Once you've checked these off, you'll see a pop up window with a link to the solution code. Note the **Diff** link: 89 | 90 | ![solutionwindow](https://d17h27t6h515a5.cloudfront.net/topher/2017/March/58bf00f9_solutionwindow/solutionwindow.png 91 | ) 92 | 93 | The **Diff** link will take you to a Github diff as seen below: 94 | ![diff](https://d17h27t6h515a5.cloudfront.net/topher/2017/March/58bf0108_diffsceenshot/diffsceenshot.png 95 | ) 96 | 97 | All of the code that was added in the solution is in green, and the removed code (which will usually be the TODO comments) is in red. 98 | ## Report Issues 99 | Notice any issues with a repository? Please file a github issue in the repository. 100 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'android-apt' 3 | 4 | android { 5 | compileSdkVersion 25 6 | buildToolsVersion "25.0.2" 7 | defaultConfig { 8 | applicationId "android.example.com.squawker" 9 | minSdkVersion 16 10 | targetSdkVersion 25 11 | versionCode 1 12 | versionName "1.0" 13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | } 22 | 23 | dependencies { 24 | compile fileTree(dir: 'libs', include: ['*.jar']) 25 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 26 | exclude group: 'com.android.support', module: 'support-annotations' 27 | }) 28 | compile 'com.android.support:appcompat-v7:25.1.0' 29 | testCompile 'junit:junit:4.12' 30 | 31 | // RecyclerView 32 | compile 'com.android.support:recyclerview-v7:25.1.0' 33 | 34 | // Schematic dependencies for ContentProvider 35 | apt 'net.simonvt.schematic:schematic-compiler:0.6.3' 36 | compile 'net.simonvt.schematic:schematic:0.6.3' 37 | 38 | // Preferences Dependencies 39 | compile 'com.android.support:preference-v7:25.1.0' 40 | 41 | // Firebase dependency 42 | compile 'com.google.firebase:firebase-messaging:10.0.1' 43 | } 44 | // Apply the Google Services plugin. Make sure to add the google-services.json file in the app 45 | // folder. You download it from the Firebase console 46 | apply plugin: 'com.google.gms.google-services' 47 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/lyla/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/android/example/com/squawker/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package android.example.com.squawker; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("android.example.com.squawker", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 27 | 28 | 31 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/AdvancedAndroid_Squawker/c8700fcc6e02acf8bead4b1787de3d9bdee45309/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /app/src/main/java/android/example/com/squawker/MainActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.example.com.squawker; 18 | 19 | import android.content.Intent; 20 | import android.database.Cursor; 21 | import android.example.com.squawker.following.FollowingPreferenceActivity; 22 | import android.example.com.squawker.provider.SquawkContract; 23 | import android.example.com.squawker.provider.SquawkProvider; 24 | import android.os.Bundle; 25 | import android.support.v4.app.LoaderManager; 26 | import android.support.v4.content.CursorLoader; 27 | import android.support.v4.content.Loader; 28 | import android.support.v7.app.AppCompatActivity; 29 | import android.support.v7.preference.PreferenceManager; 30 | import android.support.v7.widget.DividerItemDecoration; 31 | import android.support.v7.widget.LinearLayoutManager; 32 | import android.support.v7.widget.RecyclerView; 33 | import android.util.Log; 34 | import android.view.Menu; 35 | import android.view.MenuInflater; 36 | import android.view.MenuItem; 37 | 38 | import com.google.firebase.iid.FirebaseInstanceId; 39 | 40 | public class MainActivity extends AppCompatActivity implements 41 | LoaderManager.LoaderCallbacks { 42 | 43 | private static String LOG_TAG = MainActivity.class.getSimpleName(); 44 | private static final int LOADER_ID_MESSAGES = 0; 45 | 46 | RecyclerView mRecyclerView; 47 | LinearLayoutManager mLayoutManager; 48 | SquawkAdapter mAdapter; 49 | 50 | static final String[] MESSAGES_PROJECTION = { 51 | SquawkContract.COLUMN_AUTHOR, 52 | SquawkContract.COLUMN_MESSAGE, 53 | SquawkContract.COLUMN_DATE, 54 | SquawkContract.COLUMN_AUTHOR_KEY 55 | }; 56 | 57 | static final int COL_NUM_AUTHOR = 0; 58 | static final int COL_NUM_MESSAGE = 1; 59 | static final int COL_NUM_DATE = 2; 60 | static final int COL_NUM_AUTHOR_KEY = 3; 61 | 62 | 63 | @Override 64 | protected void onCreate(Bundle savedInstanceState) { 65 | super.onCreate(savedInstanceState); 66 | setContentView(R.layout.activity_main); 67 | 68 | mRecyclerView = (RecyclerView) findViewById(R.id.squawks_recycler_view); 69 | 70 | // Use this setting to improve performance if you know that changes 71 | // in content do not change the layout size of the RecyclerView 72 | mRecyclerView.setHasFixedSize(true); 73 | 74 | // Use a linear layout manager 75 | mLayoutManager = new LinearLayoutManager(this); 76 | mRecyclerView.setLayoutManager(mLayoutManager); 77 | 78 | // Add dividers 79 | DividerItemDecoration dividerItemDecoration = new DividerItemDecoration( 80 | mRecyclerView.getContext(), 81 | mLayoutManager.getOrientation()); 82 | mRecyclerView.addItemDecoration(dividerItemDecoration); 83 | 84 | // Specify an adapter 85 | mAdapter = new SquawkAdapter(); 86 | mRecyclerView.setAdapter(mAdapter); 87 | 88 | // Start the loader 89 | getSupportLoaderManager().initLoader(LOADER_ID_MESSAGES, null, this); 90 | 91 | // Get token from the ID Service you created and show it in a log 92 | String token = FirebaseInstanceId.getInstance().getToken(); 93 | String msg = getString(R.string.message_token_format, token); 94 | Log.d(LOG_TAG, msg); 95 | 96 | } 97 | 98 | @Override 99 | public boolean onCreateOptionsMenu(Menu menu) { 100 | MenuInflater inflater = getMenuInflater(); 101 | inflater.inflate(R.menu.main_menu, menu); 102 | return true; 103 | } 104 | 105 | @Override 106 | public boolean onOptionsItemSelected(MenuItem item) { 107 | int id = item.getItemId(); 108 | if (id == R.id.action_following_preferences) { 109 | // Opens the following activity when the menu icon is pressed 110 | Intent startFollowingActivity = new Intent(this, FollowingPreferenceActivity.class); 111 | startActivity(startFollowingActivity); 112 | return true; 113 | } 114 | return super.onOptionsItemSelected(item); 115 | } 116 | 117 | 118 | /** 119 | * Loader callbacks 120 | */ 121 | 122 | @Override 123 | public Loader onCreateLoader(int id, Bundle args) { 124 | // This method generates a selection off of only the current followers 125 | String selection = SquawkContract.createSelectionForCurrentFollowers( 126 | PreferenceManager.getDefaultSharedPreferences(this)); 127 | Log.d(LOG_TAG, "Selection is " + selection); 128 | return new CursorLoader(this, SquawkProvider.SquawkMessages.CONTENT_URI, 129 | MESSAGES_PROJECTION, selection, null, SquawkContract.COLUMN_DATE + " DESC"); 130 | } 131 | 132 | @Override 133 | public void onLoadFinished(Loader loader, Cursor data) { 134 | mAdapter.swapCursor(data); 135 | } 136 | 137 | @Override 138 | public void onLoaderReset(Loader loader) { 139 | mAdapter.swapCursor(null); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /app/src/main/java/android/example/com/squawker/SquawkAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package android.example.com.squawker; 18 | 19 | import android.database.Cursor; 20 | import android.example.com.squawker.provider.SquawkContract; 21 | import android.support.v7.widget.RecyclerView; 22 | import android.view.LayoutInflater; 23 | import android.view.View; 24 | import android.view.ViewGroup; 25 | import android.widget.ImageView; 26 | import android.widget.TextView; 27 | 28 | import java.text.SimpleDateFormat; 29 | import java.util.Date; 30 | 31 | /** 32 | * Converts cursor data for squawk messages into visible list items in a RecyclerView 33 | */ 34 | public class SquawkAdapter extends RecyclerView.Adapter { 35 | 36 | 37 | private Cursor mData; 38 | private static SimpleDateFormat sDateFormat = new SimpleDateFormat("dd MMM"); 39 | 40 | 41 | private static final long MINUTE_MILLIS = 1000 * 60; 42 | private static final long HOUR_MILLIS = 60 * MINUTE_MILLIS; 43 | private static final long DAY_MILLIS = 24 * HOUR_MILLIS; 44 | 45 | 46 | @Override 47 | public SquawkViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 48 | View v = LayoutInflater.from(parent.getContext()) 49 | .inflate(R.layout.item_squawk_list, parent, false); 50 | 51 | SquawkViewHolder vh = new SquawkViewHolder(v); 52 | return vh; 53 | } 54 | 55 | @Override 56 | public void onBindViewHolder(SquawkViewHolder holder, int position) { 57 | mData.moveToPosition(position); 58 | 59 | String message = mData.getString(MainActivity.COL_NUM_MESSAGE); 60 | String author = mData.getString(MainActivity.COL_NUM_AUTHOR); 61 | String authorKey = mData.getString(MainActivity.COL_NUM_AUTHOR_KEY); 62 | 63 | // Get the date for displaying 64 | long dateMillis = mData.getLong(MainActivity.COL_NUM_DATE); 65 | String date = ""; 66 | long now = System.currentTimeMillis(); 67 | 68 | // Change how the date is displayed depending on whether it was written in the last minute, 69 | // the hour, etc. 70 | if (now - dateMillis < (DAY_MILLIS)) { 71 | if (now - dateMillis < (HOUR_MILLIS)) { 72 | long minutes = Math.round((now - dateMillis) / MINUTE_MILLIS); 73 | date = String.valueOf(minutes) + "m"; 74 | } else { 75 | long minutes = Math.round((now - dateMillis) / HOUR_MILLIS); 76 | date = String.valueOf(minutes) + "h"; 77 | } 78 | } else { 79 | Date dateDate = new Date(dateMillis); 80 | date = sDateFormat.format(dateDate); 81 | } 82 | 83 | // Add a dot to the date string 84 | date = "\u2022 " + date; 85 | 86 | holder.messageTextView.setText(message); 87 | holder.authorTextView.setText(author); 88 | holder.dateTextView.setText(date); 89 | 90 | // Choose the correct, and in this case, locally stored asset for the instructor. If there 91 | // were more users, you'd probably download this as part of the message. 92 | switch (authorKey) { 93 | case SquawkContract.ASSER_KEY: 94 | holder.authorImageView.setImageResource(R.drawable.asser); 95 | break; 96 | case SquawkContract.CEZANNE_KEY: 97 | holder.authorImageView.setImageResource(R.drawable.cezanne); 98 | break; 99 | case SquawkContract.JLIN_KEY: 100 | holder.authorImageView.setImageResource(R.drawable.jlin); 101 | break; 102 | case SquawkContract.LYLA_KEY: 103 | holder.authorImageView.setImageResource(R.drawable.lyla); 104 | break; 105 | case SquawkContract.NIKITA_KEY: 106 | holder.authorImageView.setImageResource(R.drawable.nikita); 107 | break; 108 | default: 109 | holder.authorImageView.setImageResource(R.drawable.test); 110 | } 111 | } 112 | 113 | @Override 114 | public int getItemCount() { 115 | if (null == mData) return 0; 116 | return mData.getCount(); 117 | } 118 | 119 | public void swapCursor(Cursor newCursor) { 120 | mData = newCursor; 121 | notifyDataSetChanged(); 122 | } 123 | 124 | public class SquawkViewHolder extends RecyclerView.ViewHolder { 125 | final TextView authorTextView; 126 | final TextView messageTextView; 127 | final TextView dateTextView; 128 | final ImageView authorImageView; 129 | 130 | public SquawkViewHolder(View layoutView) { 131 | super(layoutView); 132 | authorTextView = (TextView) layoutView.findViewById(R.id.author_text_view); 133 | messageTextView = (TextView) layoutView.findViewById(R.id.message_text_view); 134 | dateTextView = (TextView) layoutView.findViewById(R.id.date_text_view); 135 | authorImageView = (ImageView) layoutView.findViewById( 136 | R.id.author_image_view); 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /app/src/main/java/android/example/com/squawker/fcm/SquawkFirebaseInstanceIdService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package android.example.com.squawker.fcm; 17 | 18 | import android.util.Log; 19 | 20 | import com.google.firebase.iid.FirebaseInstanceId; 21 | import com.google.firebase.iid.FirebaseInstanceIdService; 22 | 23 | /** 24 | * Listens for changes in the InstanceID 25 | */ 26 | public class SquawkFirebaseInstanceIdService extends FirebaseInstanceIdService { 27 | 28 | private static String LOG_TAG = SquawkFirebaseInstanceIdService.class.getSimpleName(); 29 | 30 | /** 31 | * Called if InstanceID token is updated. This may occur if the security of 32 | * the previous token had been compromised. Note that this is called when the InstanceID token 33 | * is initially generated so this is where you would retrieve the token. 34 | */ 35 | @Override 36 | public void onTokenRefresh() { 37 | // Get updated InstanceID token. 38 | String refreshedToken = FirebaseInstanceId.getInstance().getToken(); 39 | Log.d(LOG_TAG, "Refreshed token: " + refreshedToken); 40 | 41 | // If you want to send messages to this application instance or 42 | // manage this apps subscriptions on the server side, send the 43 | // Instance ID token to your app server. 44 | sendRegistrationToServer(refreshedToken); 45 | } 46 | 47 | /** 48 | * Persist token to third-party servers. 49 | *

50 | * Modify this method to associate the user's FCM InstanceID token with any server-side account 51 | * maintained by your application. 52 | * 53 | * @param token The new token. 54 | */ 55 | private void sendRegistrationToServer(String token) { 56 | // This method is blank, but if you were to build a server that stores users token 57 | // information, this is where you'd send the token to the server. 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /app/src/main/java/android/example/com/squawker/fcm/SquawkFirebaseMessageService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package android.example.com.squawker.fcm; 17 | 18 | import android.app.NotificationManager; 19 | import android.app.PendingIntent; 20 | import android.content.ContentValues; 21 | import android.content.Context; 22 | import android.content.Intent; 23 | import android.example.com.squawker.MainActivity; 24 | import android.example.com.squawker.R; 25 | import android.example.com.squawker.provider.SquawkContract; 26 | import android.example.com.squawker.provider.SquawkProvider; 27 | import android.media.RingtoneManager; 28 | import android.net.Uri; 29 | import android.os.AsyncTask; 30 | import android.support.v4.app.NotificationCompat; 31 | import android.util.Log; 32 | 33 | import com.google.firebase.messaging.FirebaseMessagingService; 34 | import com.google.firebase.messaging.RemoteMessage; 35 | 36 | import java.util.Map; 37 | 38 | /** 39 | * Listens for squawk FCM messages both in the background and the foreground and responds 40 | * appropriately 41 | * depending on type of message 42 | */ 43 | public class SquawkFirebaseMessageService extends FirebaseMessagingService { 44 | 45 | private static final String JSON_KEY_AUTHOR = SquawkContract.COLUMN_AUTHOR; 46 | private static final String JSON_KEY_AUTHOR_KEY = SquawkContract.COLUMN_AUTHOR_KEY; 47 | private static final String JSON_KEY_MESSAGE = SquawkContract.COLUMN_MESSAGE; 48 | private static final String JSON_KEY_DATE = SquawkContract.COLUMN_DATE; 49 | 50 | private static final int NOTIFICATION_MAX_CHARACTERS = 30; 51 | private static String LOG_TAG = SquawkFirebaseMessageService.class.getSimpleName(); 52 | 53 | /** 54 | * Called when message is received. 55 | * 56 | * @param remoteMessage Object representing the message received from Firebase Cloud Messaging 57 | */ 58 | @Override 59 | public void onMessageReceived(RemoteMessage remoteMessage) { 60 | // There are two types of messages data messages and notification messages. Data messages 61 | // are handled 62 | // here in onMessageReceived whether the app is in the foreground or background. Data 63 | // messages are the type 64 | // traditionally used with FCM. Notification messages are only received here in 65 | // onMessageReceived when the app 66 | // is in the foreground. When the app is in the background an automatically generated 67 | // notification is displayed. 68 | // When the user taps on the notification they are returned to the app. Messages 69 | // containing both notification 70 | // and data payloads are treated as notification messages. The Firebase console always 71 | // sends notification 72 | // messages. For more see: https://firebase.google.com/docs/cloud-messaging/concept-options\ 73 | 74 | // The Squawk server always sends just *data* messages, meaning that onMessageReceived when 75 | // the app is both in the foreground AND the background 76 | 77 | Log.d(LOG_TAG, "From: " + remoteMessage.getFrom()); 78 | 79 | // Check if message contains a data payload. 80 | 81 | Map data = remoteMessage.getData(); 82 | 83 | if (data.size() > 0) { 84 | Log.d(LOG_TAG, "Message data payload: " + data); 85 | 86 | // Send a notification that you got a new message 87 | sendNotification(data); 88 | insertSquawk(data); 89 | 90 | } 91 | } 92 | 93 | /** 94 | * Inserts a single squawk into the database; 95 | * 96 | * @param data Map which has the message data in it 97 | */ 98 | private void insertSquawk(final Map data) { 99 | 100 | // Database operations should not be done on the main thread 101 | AsyncTask insertSquawkTask = new AsyncTask() { 102 | 103 | @Override 104 | protected Void doInBackground(Void... voids) { 105 | ContentValues newMessage = new ContentValues(); 106 | newMessage.put(SquawkContract.COLUMN_AUTHOR, data.get(JSON_KEY_AUTHOR)); 107 | newMessage.put(SquawkContract.COLUMN_MESSAGE, data.get(JSON_KEY_MESSAGE).trim()); 108 | newMessage.put(SquawkContract.COLUMN_DATE, data.get(JSON_KEY_DATE)); 109 | newMessage.put(SquawkContract.COLUMN_AUTHOR_KEY, data.get(JSON_KEY_AUTHOR_KEY)); 110 | getContentResolver().insert(SquawkProvider.SquawkMessages.CONTENT_URI, newMessage); 111 | return null; 112 | } 113 | }; 114 | 115 | insertSquawkTask.execute(); 116 | } 117 | 118 | 119 | /** 120 | * Create and show a simple notification containing the received FCM message 121 | * 122 | * @param data Map which has the message data in it 123 | */ 124 | private void sendNotification(Map data) { 125 | Intent intent = new Intent(this, MainActivity.class); 126 | intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 127 | // Create the pending intent to launch the activity 128 | PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent, 129 | PendingIntent.FLAG_ONE_SHOT); 130 | 131 | String author = data.get(JSON_KEY_AUTHOR); 132 | String message = data.get(JSON_KEY_MESSAGE); 133 | 134 | // If the message is longer than the max number of characters we want in our 135 | // notification, truncate it and add the unicode character for ellipsis 136 | if (message.length() > NOTIFICATION_MAX_CHARACTERS) { 137 | message = message.substring(0, NOTIFICATION_MAX_CHARACTERS) + "\u2026"; 138 | } 139 | 140 | Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); 141 | NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this) 142 | .setSmallIcon(R.drawable.ic_duck) 143 | .setContentTitle(String.format(getString(R.string.notification_message), author)) 144 | .setContentText(message) 145 | .setAutoCancel(true) 146 | .setSound(defaultSoundUri) 147 | .setContentIntent(pendingIntent); 148 | 149 | NotificationManager notificationManager = 150 | (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); 151 | 152 | notificationManager.notify(0 /* ID of notification */, notificationBuilder.build()); 153 | } 154 | } -------------------------------------------------------------------------------- /app/src/main/java/android/example/com/squawker/following/FollowingPreferenceActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package android.example.com.squawker.following; 17 | 18 | import android.example.com.squawker.R; 19 | import android.os.Bundle; 20 | import android.support.v4.app.NavUtils; 21 | import android.support.v7.app.ActionBar; 22 | import android.support.v7.app.AppCompatActivity; 23 | import android.view.MenuItem; 24 | 25 | /** 26 | * Displays an activity for who you are following 27 | */ 28 | public class FollowingPreferenceActivity extends AppCompatActivity { 29 | 30 | @Override 31 | protected void onCreate(Bundle savedInstanceState) { 32 | super.onCreate(savedInstanceState); 33 | setContentView(R.layout.activity_settings); 34 | ActionBar actionBar = this.getSupportActionBar(); 35 | 36 | // Set the action bar back button to look like an up button 37 | if (actionBar != null) { 38 | actionBar.setDisplayHomeAsUpEnabled(true); 39 | } 40 | } 41 | 42 | @Override 43 | public boolean onOptionsItemSelected(MenuItem item) { 44 | int id = item.getItemId(); 45 | // When the home button is pressed, take the user back to the MainActivity 46 | if (id == android.R.id.home) { 47 | NavUtils.navigateUpFromSameTask(this); 48 | } 49 | return super.onOptionsItemSelected(item); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/android/example/com/squawker/following/FollowingPreferenceFragment.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package android.example.com.squawker.following; 17 | 18 | import android.content.SharedPreferences; 19 | import android.example.com.squawker.R; 20 | import android.os.Bundle; 21 | import android.support.v7.preference.Preference; 22 | import android.support.v7.preference.PreferenceFragmentCompat; 23 | import android.support.v7.preference.SwitchPreferenceCompat; 24 | import android.util.Log; 25 | 26 | import com.google.firebase.messaging.FirebaseMessaging; 27 | 28 | 29 | /** 30 | * Shows the list of instructors you can follow 31 | */ 32 | public class FollowingPreferenceFragment extends PreferenceFragmentCompat implements 33 | SharedPreferences.OnSharedPreferenceChangeListener { 34 | 35 | private final static String LOG_TAG = FollowingPreferenceFragment.class.getSimpleName(); 36 | 37 | @Override 38 | public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 39 | // Add visualizer preferences, defined in the XML file in res->xml->preferences_squawker 40 | addPreferencesFromResource(R.xml.following_squawker); 41 | } 42 | 43 | /** 44 | * Triggered when shared preferences changes. This will be triggered when a person is followed 45 | * or un-followed 46 | * 47 | * @param sharedPreferences SharedPreferences file 48 | * @param key The key of the preference which was changed 49 | */ 50 | @Override 51 | public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { 52 | 53 | Preference preference = findPreference(key); 54 | if (preference != null && preference instanceof SwitchPreferenceCompat) { 55 | // Get the current state of the switch preference 56 | boolean isOn = sharedPreferences.getBoolean(key, false); 57 | if (isOn) { 58 | // The preference key matches the following key for the associated instructor in 59 | // FCM. For example, the key for Lyla is key_lyla (as seen in 60 | // following_squawker.xml). The topic for Lyla's messages is /topics/key_lyla 61 | 62 | // Subscribe 63 | FirebaseMessaging.getInstance().subscribeToTopic(key); 64 | Log.d(LOG_TAG, "Subscribing to " + key); 65 | } else { 66 | // Un-subscribe 67 | FirebaseMessaging.getInstance().unsubscribeFromTopic(key); 68 | Log.d(LOG_TAG, "Un-subscribing to " + key); 69 | } 70 | } 71 | } 72 | 73 | 74 | @Override 75 | public void onCreate(Bundle savedInstanceState) { 76 | super.onCreate(savedInstanceState); 77 | // Add the shared preference change listener 78 | getPreferenceScreen().getSharedPreferences() 79 | .registerOnSharedPreferenceChangeListener(this); 80 | } 81 | 82 | @Override 83 | public void onDestroy() { 84 | super.onDestroy(); 85 | // Remove the shared preference change listener 86 | getPreferenceScreen().getSharedPreferences() 87 | .unregisterOnSharedPreferenceChangeListener(this); 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /app/src/main/java/android/example/com/squawker/provider/SquawkContract.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package android.example.com.squawker.provider; 17 | 18 | import android.content.SharedPreferences; 19 | 20 | import net.simonvt.schematic.annotation.AutoIncrement; 21 | import net.simonvt.schematic.annotation.ConflictResolutionType; 22 | import net.simonvt.schematic.annotation.DataType; 23 | import net.simonvt.schematic.annotation.NotNull; 24 | import net.simonvt.schematic.annotation.PrimaryKey; 25 | 26 | /** 27 | * Uses the Schematic (https://github.com/SimonVT/schematic) library to define the columns in a 28 | * content provider baked by a database 29 | */ 30 | 31 | public class SquawkContract { 32 | 33 | @DataType(DataType.Type.INTEGER) 34 | @PrimaryKey(onConflict = ConflictResolutionType.REPLACE) 35 | @AutoIncrement 36 | public static final String COLUMN_ID = "_id"; 37 | 38 | @DataType(DataType.Type.TEXT) 39 | @NotNull 40 | public static final String COLUMN_AUTHOR = "author"; 41 | 42 | @DataType(DataType.Type.TEXT) 43 | @NotNull 44 | public static final String COLUMN_AUTHOR_KEY = "authorKey"; 45 | 46 | @DataType(DataType.Type.TEXT) 47 | @NotNull 48 | public static final String COLUMN_MESSAGE = "message"; 49 | 50 | @DataType(DataType.Type.INTEGER) 51 | @NotNull 52 | public static final String COLUMN_DATE = "date"; 53 | 54 | 55 | // Topic keys as matching what is found in the database 56 | public static final String ASSER_KEY = "key_asser"; 57 | public static final String CEZANNE_KEY = "key_cezanne"; 58 | public static final String JLIN_KEY = "key_jlin"; 59 | public static final String LYLA_KEY = "key_lyla"; 60 | public static final String NIKITA_KEY = "key_nikita"; 61 | public static final String TEST_ACCOUNT_KEY = "key_test"; 62 | 63 | 64 | public static final String[] INSTRUCTOR_KEYS = { 65 | ASSER_KEY, CEZANNE_KEY, JLIN_KEY, LYLA_KEY, NIKITA_KEY 66 | }; 67 | 68 | /** 69 | * Creates a SQLite SELECTION parameter that filters just the rows for the authors you are 70 | * currently following. 71 | */ 72 | public static String createSelectionForCurrentFollowers(SharedPreferences preferences) { 73 | 74 | StringBuilder stringBuilder = new StringBuilder(); 75 | //Automatically add the test account 76 | stringBuilder.append(COLUMN_AUTHOR_KEY).append(" IN ('").append(TEST_ACCOUNT_KEY).append("'"); 77 | 78 | for (String key : INSTRUCTOR_KEYS) { 79 | if (preferences.getBoolean(key, false)) { 80 | stringBuilder.append(","); 81 | stringBuilder.append("'").append(key).append("'"); 82 | } 83 | } 84 | stringBuilder.append(")"); 85 | return stringBuilder.toString(); 86 | } 87 | } -------------------------------------------------------------------------------- /app/src/main/java/android/example/com/squawker/provider/SquawkDatabase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package android.example.com.squawker.provider; 17 | 18 | import net.simonvt.schematic.annotation.Database; 19 | import net.simonvt.schematic.annotation.Table; 20 | 21 | /** 22 | * Uses the Schematic (https://github.com/SimonVT/schematic) library to create a database with one 23 | * table for messages 24 | */ 25 | 26 | @Database(version = SquawkDatabase.VERSION) 27 | public class SquawkDatabase { 28 | 29 | public static final int VERSION = 4; 30 | 31 | @Table(SquawkContract.class) 32 | public static final String SQUAWK_MESSAGES = "squawk_messages"; 33 | 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/android/example/com/squawker/provider/SquawkProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2017 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package android.example.com.squawker.provider; 17 | 18 | import android.net.Uri; 19 | 20 | import net.simonvt.schematic.annotation.ContentProvider; 21 | import net.simonvt.schematic.annotation.ContentUri; 22 | import net.simonvt.schematic.annotation.TableEndpoint; 23 | 24 | /** 25 | * Uses the Schematic (https://github.com/SimonVT/schematic) to create a content provider and 26 | * define 27 | * URIs for the provider 28 | */ 29 | 30 | @ContentProvider( 31 | authority = SquawkProvider.AUTHORITY, 32 | database = SquawkDatabase.class) 33 | public final class SquawkProvider { 34 | 35 | public static final String AUTHORITY = "android.example.com.squawker.provider.provider"; 36 | 37 | 38 | @TableEndpoint(table = SquawkDatabase.SQUAWK_MESSAGES) 39 | public static class SquawkMessages { 40 | 41 | @ContentUri( 42 | path = "messages", 43 | type = "vnd.android.cursor.dir/messages", 44 | defaultSort = SquawkContract.COLUMN_DATE + " DESC") 45 | public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/messages"); 46 | } 47 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi-v11/ic_duck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/AdvancedAndroid_Squawker/c8700fcc6e02acf8bead4b1787de3d9bdee45309/app/src/main/res/drawable-hdpi-v11/ic_duck.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi-v9/ic_duck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/AdvancedAndroid_Squawker/c8700fcc6e02acf8bead4b1787de3d9bdee45309/app/src/main/res/drawable-hdpi-v9/ic_duck.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_duck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/AdvancedAndroid_Squawker/c8700fcc6e02acf8bead4b1787de3d9bdee45309/app/src/main/res/drawable-hdpi/ic_duck.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_follow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/AdvancedAndroid_Squawker/c8700fcc6e02acf8bead4b1787de3d9bdee45309/app/src/main/res/drawable-hdpi/ic_follow.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi-v11/ic_duck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/AdvancedAndroid_Squawker/c8700fcc6e02acf8bead4b1787de3d9bdee45309/app/src/main/res/drawable-mdpi-v11/ic_duck.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi-v9/ic_duck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/AdvancedAndroid_Squawker/c8700fcc6e02acf8bead4b1787de3d9bdee45309/app/src/main/res/drawable-mdpi-v9/ic_duck.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_duck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/AdvancedAndroid_Squawker/c8700fcc6e02acf8bead4b1787de3d9bdee45309/app/src/main/res/drawable-mdpi/ic_duck.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_follow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/AdvancedAndroid_Squawker/c8700fcc6e02acf8bead4b1787de3d9bdee45309/app/src/main/res/drawable-mdpi/ic_follow.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi-v11/ic_duck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/AdvancedAndroid_Squawker/c8700fcc6e02acf8bead4b1787de3d9bdee45309/app/src/main/res/drawable-xhdpi-v11/ic_duck.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi-v9/ic_duck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/AdvancedAndroid_Squawker/c8700fcc6e02acf8bead4b1787de3d9bdee45309/app/src/main/res/drawable-xhdpi-v9/ic_duck.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_duck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/AdvancedAndroid_Squawker/c8700fcc6e02acf8bead4b1787de3d9bdee45309/app/src/main/res/drawable-xhdpi/ic_duck.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_follow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/AdvancedAndroid_Squawker/c8700fcc6e02acf8bead4b1787de3d9bdee45309/app/src/main/res/drawable-xhdpi/ic_follow.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi-v11/ic_duck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/AdvancedAndroid_Squawker/c8700fcc6e02acf8bead4b1787de3d9bdee45309/app/src/main/res/drawable-xxhdpi-v11/ic_duck.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi-v9/ic_duck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/AdvancedAndroid_Squawker/c8700fcc6e02acf8bead4b1787de3d9bdee45309/app/src/main/res/drawable-xxhdpi-v9/ic_duck.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_duck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/AdvancedAndroid_Squawker/c8700fcc6e02acf8bead4b1787de3d9bdee45309/app/src/main/res/drawable-xxhdpi/ic_duck.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_follow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/AdvancedAndroid_Squawker/c8700fcc6e02acf8bead4b1787de3d9bdee45309/app/src/main/res/drawable-xxhdpi/ic_follow.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/asser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/AdvancedAndroid_Squawker/c8700fcc6e02acf8bead4b1787de3d9bdee45309/app/src/main/res/drawable/asser.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/cezanne.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/AdvancedAndroid_Squawker/c8700fcc6e02acf8bead4b1787de3d9bdee45309/app/src/main/res/drawable/cezanne.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_person_black_48dp.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/jlin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/AdvancedAndroid_Squawker/c8700fcc6e02acf8bead4b1787de3d9bdee45309/app/src/main/res/drawable/jlin.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/lyla.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/AdvancedAndroid_Squawker/c8700fcc6e02acf8bead4b1787de3d9bdee45309/app/src/main/res/drawable/lyla.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/nikita.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/AdvancedAndroid_Squawker/c8700fcc6e02acf8bead4b1787de3d9bdee45309/app/src/main/res/drawable/nikita.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/AdvancedAndroid_Squawker/c8700fcc6e02acf8bead4b1787de3d9bdee45309/app/src/main/res/drawable/test.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 18 | 19 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_squawk_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 22 | 23 | 29 | 30 | 41 | 42 | 49 | 50 | 51 | 62 | 63 | -------------------------------------------------------------------------------- /app/src/main/res/menu/main_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 |

16 | 22 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/AdvancedAndroid_Squawker/c8700fcc6e02acf8bead4b1787de3d9bdee45309/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/AdvancedAndroid_Squawker/c8700fcc6e02acf8bead4b1787de3d9bdee45309/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/AdvancedAndroid_Squawker/c8700fcc6e02acf8bead4b1787de3d9bdee45309/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/AdvancedAndroid_Squawker/c8700fcc6e02acf8bead4b1787de3d9bdee45309/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/udacity/AdvancedAndroid_Squawker/c8700fcc6e02acf8bead4b1787de3d9bdee45309/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/bool.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | false 14 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | #FFC107 14 | #FFA000 15 | #8BC34A 16 | #FFECB3 17 | #212121 18 | #757575 19 | #BDBDBD 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 16dp 14 | 16dp 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 11 | 12 | Squawker 13 | 14 | 15 | Following 16 | 17 | 18 | "New squawk from %1$s" 19 | Refreshing Data 20 | InstanceID Token: %s 21 | 22 | 23 | key_asser 24 | key_cezanne 25 | key_jlin 26 | key_lyla 27 | key_nikita 28 | 29 | Asser 30 | Cezanne 31 | Jessica 32 | Lyla 33 | Nikita 34 | 35 | Not Following 36 | Following 37 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/xml/following_squawker.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | 20 | 21 | 27 | 28 | 34 | 35 | 41 | 42 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /app/src/test/java/android/example/com/squawker/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package android.example.com.squawker; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.neenbedankt.gradle.plugins:android-apt:1.2' 9 | classpath 'com.android.tools.build:gradle:2.2.3' 10 | 11 | // Google Services plugin 12 | classpath 'com.google.gms:google-services:3.0.0' 13 | // NOTE: Do not place your application dependencies here; they belong 14 | // in the individual module build.gradle files 15 | } 16 | 17 | } 18 | 19 | allprojects { 20 | repositories { 21 | jcenter() 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } 28 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 28 10:00:20 PST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | --------------------------------------------------------------------------------