Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
.DS_Store

# local keystore files
keystore.properties

# built application files
*.apk
*.ap_
Expand Down
105 changes: 104 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,110 @@
# ShushMe
Google Places API demo app

This is the toy app for the Places lesson of the [Advanced Android App Development course on Udacity](https://www.udacity.com/course/advanced-android-app-development--ud855).


## Screenshots

![Screenshot1](screenshots/screen_1.png) ![Screenshot2](screenshots/screen_2.png) ![Screenshot3](screenshots/screen_3.png)
![Screenshot4](screenshots/screen_4.png) ![Screenshot5](screenshots/screen_5.png) ![Screenshot6](screenshots/screen_6.png)


## How to use this repo while taking the course

First create **keystore.properties** file in root folder and add following line -

```
GOOGLE_API_KEY="insert-your-api-key-here"
```

Each code repository in this class has a chain of commits that looks like this:

![listofcommits](https://d17h27t6h515a5.cloudfront.net/topher/2017/March/58befe2e_listofcommits/listofcommits.png)

These commits show every step you'll take to create the app. They include **Exercise** commits and **Solution** commits.

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.

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.

Each commit also has a **branch** associated with it of the same name as the commit message, seen below:

![branches](https://d17h27t6h515a5.cloudfront.net/topher/2017/April/590390fe_branches-ud855/branches-ud855.png
)
Access all branches from this tab

![listofbranches](https://d17h27t6h515a5.cloudfront.net/topher/2017/March/58befe76_listofbranches/listofbranches.png
)


![branchesdropdown](https://d17h27t6h515a5.cloudfront.net/topher/2017/April/590391a3_branches-dropdown-ud855/branches-dropdown-ud855.png
)


The branches are also accessible from the drop-down in the "Code" tab


## Working with the Course Code

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.

The basic steps are:

1. Clone the repo
2. Checkout the exercise branch
3. Find and complete the TODOs
4. Optionally commit your code changes
5. Compare with the solution


**Step 1: Clone the repo**

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:

```bash
git clone https://github.com/udacity/REPOSITORY_NAME.git
```

**Step 2: Checkout the exercise branch**

As you do different exercises in the code, you'll be told which exercise you're on, as seen below:
![exerciseexample](https://d17h27t6h515a5.cloudfront.net/topher/2017/March/58bf0087_exerciseexample/exerciseexample.png
)

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:

```bash
git checkout TFCM.01-Exercise-AddGradleDependencies
```

**Step 3: Find and complete the TODOs**

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.

We've numbered the TODO steps so you can do them in order:
![todos](https://d17h27t6h515a5.cloudfront.net/topher/2017/March/58bf00e7_todos/todos.png
)

**Step 4: Optionally commit your code changes**

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.

```bash
git add .
git commit -m "Your commit message"
```

**Step 5: Compare with the solution**

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:

![solutionwindow](https://d17h27t6h515a5.cloudfront.net/topher/2017/March/58bf00f9_solutionwindow/solutionwindow.png
)

The **Diff** link will take you to a Github diff as seen below:
![diff](https://d17h27t6h515a5.cloudfront.net/topher/2017/March/58bf0108_diffsceenshot/diffsceenshot.png
)

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.
## Report Issues
Notice any issues with a repository? Please file a github issue in the repository.
24 changes: 18 additions & 6 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
apply plugin: 'com.android.application'

def keystorePropertiesFile = rootProject.file("keystore.properties")
def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))

android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
compileSdkVersion 26
buildToolsVersion "26.0.3"
defaultConfig {
applicationId "com.example.android.shushme"
minSdkVersion 16
targetSdkVersion 25
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
Expand All @@ -17,15 +21,23 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}

buildTypes.each {
it.resValue 'string', 'GOOGLE_API_KEY', keystoreProperties['GOOGLE_API_KEY']
}
}

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.1.0'
compile 'com.android.support:recyclerview-v7:25.0.1'
// TODO (3) Add play-services-places and play-services-location dependencies
compile 'com.android.support:appcompat-v7:26.1.0'
compile 'com.android.support:recyclerview-v7:26.1.0'

//Google Play Services
compile 'com.google.android.gms:play-services-places:11.6.2'
compile 'com.google.android.gms:play-services-location:11.6.2'

testCompile 'junit:junit:4.12'
}
14 changes: 11 additions & 3 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,22 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.shushme">

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

<!-- TODO COMPLETED BUG FIXED BY ADDING PERMISSION NOT MENTIONED IN EXERCISE -->
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY"/>

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">

<!-- TODO (1) Add meta-data tag with API key -->
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="@string/GOOGLE_API_KEY" />

<activity android:name="com.example.android.shushme.MainActivity">
<intent-filter>
Expand All @@ -23,8 +31,8 @@
android:authorities="com.example.android.shushme"
android:exported="false"/>

</application>
<receiver android:name=".GeofenceBroadcastReceiver" />

<!-- TODO (2) Add uses-permission tags for INTERNET and ACCESS_FINE_LOCATION -->
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package com.example.android.shushme;

/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.media.AudioManager;
import android.support.v4.app.TaskStackBuilder;
import android.support.v7.app.NotificationCompat;
import android.util.Log;

import com.google.android.gms.location.Geofence;
import com.google.android.gms.location.GeofencingEvent;

public class GeofenceBroadcastReceiver extends BroadcastReceiver {

public static final String TAG = GeofenceBroadcastReceiver.class.getSimpleName();

/***
* Handles the Broadcast message sent when the Geofence Transition is triggered
* Careful here though, this is running on the main thread so make sure you start an AsyncTask for
* anything that takes longer than say 10 second to run
*
* @param context
* @param intent
*/
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG, "-> onReceive called");

// TODO COMPLETED (4) Use GeofencingEvent.fromIntent to retrieve the GeofencingEvent that caused the transition
// Get the Geofence Event from the Intent sent through

GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
if (geofencingEvent.hasError()) {
Log.e(TAG, String.format("-> Error code : %d", geofencingEvent.getErrorCode()));
return;
}

// TODO COMPLETED (5) Call getGeofenceTransition to get the transition type and use AudioManager to set the
// phone ringer mode based on the transition type. Feel free to create a helper method (setRingerMode)

// Get the transition type.
int geofenceTransition = geofencingEvent.getGeofenceTransition();
// Check which transition type has triggered this event
if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER) {
setRingerMode(context, AudioManager.RINGER_MODE_SILENT);
} else if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {
setRingerMode(context, AudioManager.RINGER_MODE_NORMAL);
} else {
// Log the error.
Log.e(TAG, String.format("-> Unknown transition : %d", geofenceTransition));
// No need to do anything else
return;
}

// TODO COMPLETED (6) Show a notification to alert the user that the ringer mode has changed.
// Feel free to create a helper method (sendNotification)

// Send the notification
sendNotification(context, geofenceTransition);
}


/**
* Posts a notification in the notification bar when a transition is detected
* Uses different icon drawables for different transition types
* If the user clicks the notification, control goes to the MainActivity
*
* @param context The calling context for building a task stack
* @param transitionType The geofence transition type, can be Geofence.GEOFENCE_TRANSITION_ENTER
* or Geofence.GEOFENCE_TRANSITION_EXIT
*/
private void sendNotification(Context context, int transitionType) {
Log.v(TAG, "-> sendNotification -> transitionType = " + transitionType);

// Create an explicit content Intent that starts the main Activity.
Intent notificationIntent = new Intent(context, MainActivity.class);

// Construct a task stack.
TaskStackBuilder stackBuilder = TaskStackBuilder.create(context);

// Add the main Activity to the task stack as the parent.
stackBuilder.addParentStack(MainActivity.class);

// Push the content Intent onto the stack.
stackBuilder.addNextIntent(notificationIntent);

// Get a PendingIntent containing the entire back stack.
PendingIntent notificationPendingIntent =
stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);

// Get a notification builder
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);

// Check the transition type to display the relevant icon image
if (transitionType == Geofence.GEOFENCE_TRANSITION_ENTER) {
builder.setSmallIcon(R.drawable.ic_volume_off_white_24dp)
.setLargeIcon(BitmapFactory.decodeResource(context.getResources(),
R.drawable.ic_volume_off_white_24dp))
.setContentTitle(context.getString(R.string.silent_mode_activated));
} else if (transitionType == Geofence.GEOFENCE_TRANSITION_EXIT) {
builder.setSmallIcon(R.drawable.ic_volume_up_white_24dp)
.setLargeIcon(BitmapFactory.decodeResource(context.getResources(),
R.drawable.ic_volume_up_white_24dp))
.setContentTitle(context.getString(R.string.back_to_normal));
}

// Continue building the notification
builder.setContentText(context.getString(R.string.touch_to_relaunch));
builder.setContentIntent(notificationPendingIntent);

// Dismiss notification once the user touches it.
builder.setAutoCancel(true);

// Get an instance of the Notification manager
NotificationManager mNotificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

// Issue the notification
mNotificationManager.notify(0, builder.build());
}

/**
* Changes the ringer mode on the device to either silent or back to normal
*
* @param context The context to access AUDIO_SERVICE
* @param mode The desired mode to switch device to, can be AudioManager.RINGER_MODE_SILENT or
* AudioManager.RINGER_MODE_NORMAL
*/
private void setRingerMode(Context context, int mode) {
Log.i(TAG, "-> setRingerMode -> mode = " + mode);

NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

// TODO COMPLETED - BUG FIXED check https://github.com/udacity/AdvancedAndroid_Shushme/issues/2

// Check for DND permissions for API 24+
if (android.os.Build.VERSION.SDK_INT < 24 ||
(android.os.Build.VERSION.SDK_INT >= 24 && nm.isNotificationPolicyAccessGranted())) {
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
audioManager.setRingerMode(mode);
}
}
}
Loading