├── .gitignore ├── .idea ├── caches │ └── build_file_checksums.ser ├── codeStyles │ └── Project.xml ├── gradle.xml ├── misc.xml └── runConfigurations.xml ├── app ├── .gitignore ├── build.gradle ├── google-services.json ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── codingwithmitch │ │ └── googledirectionstest │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── codingwithmitch │ │ │ └── googledirectionstest │ │ │ ├── Constants.java │ │ │ ├── Topics.java │ │ │ ├── UserClient.java │ │ │ ├── adapters │ │ │ ├── ChatMessageRecyclerAdapter.java │ │ │ ├── ChatroomRecyclerAdapter.java │ │ │ ├── ImageListRecyclerAdapter.java │ │ │ └── UserRecyclerAdapter.java │ │ │ ├── models │ │ │ ├── ChatMessage.java │ │ │ ├── Chatroom.java │ │ │ ├── ClusterMarker.java │ │ │ ├── PolylineData.java │ │ │ ├── User.java │ │ │ ├── UserLocation.java │ │ │ └── UserMarker.java │ │ │ ├── services │ │ │ └── LocationService.java │ │ │ ├── ui │ │ │ ├── ChatroomActivity.java │ │ │ ├── IProfile.java │ │ │ ├── ImageListFragment.java │ │ │ ├── LoginActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── ProfileActivity.java │ │ │ ├── RegisterActivity.java │ │ │ └── UserListFragment.java │ │ │ └── util │ │ │ ├── Check.java │ │ │ ├── MyClusterManagerRenderer.java │ │ │ └── ViewWeightAnimationWrapper.java │ └── res │ │ ├── anim │ │ ├── slide_in_down.xml │ │ ├── slide_in_up.xml │ │ ├── slide_out_down.xml │ │ └── slide_out_up.xml │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── cartman_cop.jpg │ │ ├── chef.png │ │ ├── cwm_logo.png │ │ ├── eric_cartman.jpg │ │ ├── grey_border_top.xml │ │ ├── ic_add_white_24dp.xml │ │ ├── ic_check_green_24dp.xml │ │ ├── ic_full_screen_black_24dp.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_refresh_black_24dp.xml │ │ ├── ike.jpg │ │ ├── kyle.jpg │ │ ├── satan.jpg │ │ ├── theme_button.xml │ │ └── tweek.jpg │ │ ├── layout │ │ ├── activity_chatroom.xml │ │ ├── activity_login.xml │ │ ├── activity_main.xml │ │ ├── activity_profile.xml │ │ ├── activity_register.xml │ │ ├── fragment_image_list.xml │ │ ├── fragment_user_list.xml │ │ ├── layout_chat_message_list_item.xml │ │ ├── layout_chatroom_list_item.xml │ │ ├── layout_custom_marker.xml │ │ ├── layout_image_list_item.xml │ │ └── layout_user_list_item.xml │ │ ├── menu │ │ ├── chatroom_menu.xml │ │ └── menu_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── dimen.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── codingwithmitch │ └── googledirectionstest │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/libraries 5 | /.idea/modules.xml 6 | /.idea/workspace.xml 7 | .DS_Store 8 | /build 9 | /captures 10 | .externalNativeBuild 11 | -------------------------------------------------------------------------------- /.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/GoogleMaps2018-Test/fbc16ca897ced730deffd96a7718c879e1a9c691/.idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 27 | 28 | 29 | 30 | 31 | 32 | 34 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 27 5 | defaultConfig { 6 | applicationId "com.codingwithmitch.googledirectionstest" 7 | minSdkVersion 15 8 | targetSdkVersion 27 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | multiDexEnabled true 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | implementation fileTree(dir: 'libs', include: ['*.jar']) 24 | implementation 'com.android.support:appcompat-v7:27.1.1' 25 | implementation 'com.android.support.constraint:constraint-layout:1.1.2' 26 | implementation 'com.android.support:support-v4:27.1.1' 27 | testImplementation 'junit:junit:4.12' 28 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 29 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 30 | 31 | //Android Support Design Library 32 | implementation 'com.android.support:design:27.1.1' 33 | //RecyclerView 34 | implementation 'com.android.support:recyclerview-v7:27.1.1' 35 | 36 | // Support multidex 37 | implementation 'com.android.support:multidex:1.0.3' 38 | 39 | // Firebase Core 40 | implementation 'com.google.firebase:firebase-core:16.0.1' 41 | 42 | //Firebase Authentication 43 | implementation 'com.google.firebase:firebase-auth:16.0.2' 44 | 45 | // Firestore Firestore 46 | implementation 'com.google.firebase:firebase-firestore:17.0.4' 47 | 48 | //Google Play Services for Maps 49 | implementation 'com.google.android.gms:play-services-maps:15.0.1' 50 | 51 | // Maps utils (required for custom markers) 52 | implementation 'com.google.maps.android:android-maps-utils:0.5+' 53 | 54 | // Google play services for location information 55 | implementation 'com.google.android.gms:play-services-location:15.0.1' 56 | 57 | // Google Maps Services (needed for directions) 58 | implementation 'com.google.maps:google-maps-services:0.2.9' 59 | implementation 'org.slf4j:slf4j-nop:1.7.25' 60 | 61 | // glide 62 | implementation 'com.github.bumptech.glide:glide:4.8.0' 63 | annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0' 64 | 65 | // Circle ImageView 66 | implementation 'de.hdodenhof:circleimageview:2.2.0' 67 | 68 | } 69 | apply plugin: 'com.google.gms.google-services' 70 | -------------------------------------------------------------------------------- /app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "482608123904", 4 | "firebase_url": "https://silver-harmony-212817.firebaseio.com", 5 | "project_id": "silver-harmony-212817", 6 | "storage_bucket": "silver-harmony-212817.appspot.com" 7 | }, 8 | "client": [ 9 | { 10 | "client_info": { 11 | "mobilesdk_app_id": "1:482608123904:android:6a2adede06d9e24e", 12 | "android_client_info": { 13 | "package_name": "com.codingwithmitch.googledirectionstest" 14 | } 15 | }, 16 | "oauth_client": [ 17 | { 18 | "client_id": "482608123904-eulmj1657vrl29udm779lia0kdcm4siv.apps.googleusercontent.com", 19 | "client_type": 1, 20 | "android_info": { 21 | "package_name": "com.codingwithmitch.googledirectionstest", 22 | "certificate_hash": "5452be846acdcd729bb27a60a61dabfe596954c4" 23 | } 24 | }, 25 | { 26 | "client_id": "482608123904-5ci8llit1l8kgagqksl20k4q0q5htjof.apps.googleusercontent.com", 27 | "client_type": 3 28 | } 29 | ], 30 | "api_key": [ 31 | { 32 | "current_key": "AIzaSyCNU8TSa18XAjs5kfsMsvQ-yw756pAS5Hc" 33 | } 34 | ], 35 | "services": { 36 | "analytics_service": { 37 | "status": 1 38 | }, 39 | "appinvite_service": { 40 | "status": 2, 41 | "other_platform_oauth_client": [ 42 | { 43 | "client_id": "482608123904-5ci8llit1l8kgagqksl20k4q0q5htjof.apps.googleusercontent.com", 44 | "client_type": 3 45 | } 46 | ] 47 | }, 48 | "ads_service": { 49 | "status": 2 50 | } 51 | } 52 | } 53 | ], 54 | "configuration_version": "1" 55 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/codingwithmitch/googledirectionstest/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.codingwithmitch.googledirectionstest; 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 | * Instrumented 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() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.codingwithmitch.googledirectionstest", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 32 | 35 | 38 | 39 | 42 | 43 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/codingwithmitch/googledirectionstest/Constants.java: -------------------------------------------------------------------------------- 1 | package com.codingwithmitch.googledirectionstest; 2 | 3 | public class Constants { 4 | 5 | public static final int ERROR_DIALOG_REQUEST = 9001; 6 | public static final String MAPVIEW_BUNDLE_KEY = "com.codingwithmitch.googledirectionstest.MAPVIEW_BUNDLE_KEY"; 7 | public static final int PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 9002; 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/codingwithmitch/googledirectionstest/Topics.java: -------------------------------------------------------------------------------- 1 | package com.codingwithmitch.googledirectionstest; 2 | 3 | public class Topics { 4 | 5 | // Making sure the device can use google maps 6 | // Enabling location information 7 | // Enabling Google Maps and other API's from Google Cloud Console 8 | // Getting started with a Google map (Using the API key and inflating a MapView 9 | // (chose to use MapView instead of MapFragment https://developers.google.com/maps/documentation/android-sdk/map#the_map_object) 10 | // Get last known location and upload to Firebase 11 | // Continue retrieving last known location every 5 seconds using a service 12 | // Upload that location to Firebase 13 | // Retrieve location of everyone in a chatroom from Firebase every 5 seconds 14 | // Add markers for every person in chatroom 15 | // animating the camera movement 16 | // Setting view bounds 17 | // ClickListeners for markers 18 | // Retrieving distance and travel duration 19 | // Creating info windows when markers are clicked 20 | // Displaying distance and travel duration in an info window 21 | // Calculating different possible routes 22 | // Add polylines for routes 23 | // Hiding markers 24 | // removing markers 25 | // Initiating a trip in Google Maps app when a trip is selected 26 | // Enabling traffic information (Unfortunately there's not much we can do here) 27 | // Custom icons for the map (Creating a clusterItem and all that stuff) 28 | // https://developers.google.com/maps/documentation/android-sdk/utility/marker-clustering 29 | // https://stackoverflow.com/questions/32158927/android-google-map-icongenerator-making-a-transparent-marker-icon 30 | // Stopping the Location service when user has signed out (checking for null pointer in "saveUserLocation" method in service 31 | // Make sure to talk about the UserClient thing 32 | 33 | 34 | 35 | // MIGHT DO THIS: 36 | // 1) show how to get location simply using "getLastKnownLocation" 37 | // 2) show how to get location updates using a LocationRequest 38 | // 3) show how to get location updates using a service that automatically updates the database if app in background or foreground 39 | // (This also uses the LocationRequest) If greater than API 26 we can use a background service that lasts until app is closed. 40 | 41 | 42 | // Notes: 43 | // 1) After polylines have been added, the positions will no longer change on the map 44 | // This can be reset by pressing the reset button in the top left 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/codingwithmitch/googledirectionstest/UserClient.java: -------------------------------------------------------------------------------- 1 | package com.codingwithmitch.googledirectionstest; 2 | 3 | import android.app.Application; 4 | 5 | import com.codingwithmitch.googledirectionstest.models.User; 6 | import com.codingwithmitch.googledirectionstest.services.LocationService; 7 | import com.google.firebase.firestore.FirebaseFirestore; 8 | 9 | public class UserClient extends Application { 10 | 11 | private User user = null; 12 | 13 | public User getUser() { 14 | return user; 15 | } 16 | 17 | public void setUser(User user) { 18 | this.user = user; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/codingwithmitch/googledirectionstest/adapters/ChatMessageRecyclerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.codingwithmitch.googledirectionstest.adapters; 2 | 3 | import android.content.Context; 4 | import android.support.annotation.NonNull; 5 | import android.support.v4.content.ContextCompat; 6 | import android.support.v7.widget.RecyclerView; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.TextView; 11 | 12 | import com.codingwithmitch.googledirectionstest.R; 13 | import com.codingwithmitch.googledirectionstest.models.ChatMessage; 14 | import com.codingwithmitch.googledirectionstest.models.Chatroom; 15 | import com.codingwithmitch.googledirectionstest.models.User; 16 | import com.google.android.gms.tasks.OnCompleteListener; 17 | import com.google.android.gms.tasks.Task; 18 | import com.google.firebase.auth.FirebaseAuth; 19 | import com.google.firebase.firestore.DocumentReference; 20 | import com.google.firebase.firestore.DocumentSnapshot; 21 | import com.google.firebase.firestore.FirebaseFirestore; 22 | 23 | import java.lang.reflect.Array; 24 | import java.util.ArrayList; 25 | 26 | public class ChatMessageRecyclerAdapter extends RecyclerView.Adapter{ 27 | 28 | private ArrayList mMessages = new ArrayList<>(); 29 | private ArrayList mUsers = new ArrayList<>(); 30 | private Context mContext; 31 | 32 | public ChatMessageRecyclerAdapter(ArrayList messages, 33 | ArrayList users, 34 | Context context) { 35 | this.mMessages = messages; 36 | this.mUsers = users; 37 | this.mContext = context; 38 | } 39 | 40 | @NonNull 41 | @Override 42 | public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 43 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_chat_message_list_item, parent, false); 44 | final ViewHolder holder = new ViewHolder(view); 45 | return holder; 46 | } 47 | 48 | @Override 49 | public void onBindViewHolder(@NonNull final ViewHolder holder, final int position) { 50 | 51 | 52 | if(FirebaseAuth.getInstance().getUid().equals(mMessages.get(position).getUser().getUser_id())){ 53 | ((ViewHolder)holder).username.setTextColor(ContextCompat.getColor(mContext, R.color.green1)); 54 | } 55 | else{ 56 | ((ViewHolder)holder).username.setTextColor(ContextCompat.getColor(mContext, R.color.blue2)); 57 | } 58 | 59 | ((ViewHolder)holder).username.setText(mMessages.get(position).getUser().getUsername()); 60 | ((ViewHolder)holder).message.setText(mMessages.get(position).getMessage()); 61 | } 62 | 63 | 64 | 65 | @Override 66 | public int getItemCount() { 67 | return mMessages.size(); 68 | } 69 | 70 | public class ViewHolder extends RecyclerView.ViewHolder 71 | { 72 | TextView message, username; 73 | 74 | public ViewHolder(View itemView) { 75 | super(itemView); 76 | message = itemView.findViewById(R.id.chat_message_message); 77 | username = itemView.findViewById(R.id.chat_message_username); 78 | } 79 | } 80 | 81 | 82 | } 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /app/src/main/java/com/codingwithmitch/googledirectionstest/adapters/ChatroomRecyclerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.codingwithmitch.googledirectionstest.adapters; 2 | 3 | import android.support.annotation.NonNull; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.TextView; 9 | 10 | import com.codingwithmitch.googledirectionstest.R; 11 | import com.codingwithmitch.googledirectionstest.models.Chatroom; 12 | 13 | import java.util.ArrayList; 14 | import java.util.HashSet; 15 | import java.util.Set; 16 | 17 | public class ChatroomRecyclerAdapter extends RecyclerView.Adapter{ 18 | 19 | private ArrayList mChatrooms = new ArrayList<>(); 20 | private ChatroomRecyclerClickListener mChatroomRecyclerClickListener; 21 | 22 | public ChatroomRecyclerAdapter(ArrayList chatrooms, ChatroomRecyclerClickListener chatroomRecyclerClickListener) { 23 | this.mChatrooms = chatrooms; 24 | mChatroomRecyclerClickListener = chatroomRecyclerClickListener; 25 | } 26 | 27 | // private Set mChatrooms = new HashSet<>(); 28 | // private ChatroomRecyclerClickListener mChatroomRecyclerClickListener; 29 | // 30 | // public ChatroomRecyclerAdapter(Set chatrooms, ChatroomRecyclerClickListener chatroomRecyclerClickListener) { 31 | // this.mChatrooms = chatrooms; 32 | // mChatroomRecyclerClickListener = chatroomRecyclerClickListener; 33 | // } 34 | 35 | @NonNull 36 | @Override 37 | public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 38 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_chatroom_list_item, parent, false); 39 | final ViewHolder holder = new ViewHolder(view, mChatroomRecyclerClickListener); 40 | 41 | 42 | return holder; 43 | } 44 | 45 | @Override 46 | public void onBindViewHolder(@NonNull ViewHolder holder, int position) { 47 | 48 | // ((ViewHolder)holder).chatroomTitle.setText(((Chatroom)(mChatrooms.toArray()[position])).getTitle()); 49 | ((ViewHolder)holder).chatroomTitle.setText(mChatrooms.get(position).getTitle()); 50 | } 51 | 52 | @Override 53 | public int getItemCount() { 54 | return mChatrooms.size(); 55 | } 56 | 57 | public class ViewHolder extends RecyclerView.ViewHolder implements 58 | View.OnClickListener 59 | { 60 | TextView chatroomTitle; 61 | ChatroomRecyclerClickListener clickListener; 62 | 63 | public ViewHolder(View itemView, ChatroomRecyclerClickListener clickListener) { 64 | super(itemView); 65 | chatroomTitle = itemView.findViewById(R.id.chatroom_title); 66 | this.clickListener = clickListener; 67 | itemView.setOnClickListener(this); 68 | } 69 | 70 | @Override 71 | public void onClick(View v) { 72 | clickListener.onChatroomSelected(getAdapterPosition()); 73 | } 74 | } 75 | 76 | public interface ChatroomRecyclerClickListener { 77 | public void onChatroomSelected(int position); 78 | } 79 | } 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /app/src/main/java/com/codingwithmitch/googledirectionstest/adapters/ImageListRecyclerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.codingwithmitch.googledirectionstest.adapters; 2 | 3 | import android.content.Context; 4 | import android.graphics.drawable.Drawable; 5 | import android.support.annotation.NonNull; 6 | import android.support.v7.widget.RecyclerView; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.ImageView; 11 | import android.widget.TextView; 12 | 13 | import com.bumptech.glide.Glide; 14 | import com.bumptech.glide.request.RequestOptions; 15 | import com.codingwithmitch.googledirectionstest.R; 16 | import com.codingwithmitch.googledirectionstest.models.User; 17 | 18 | import java.util.ArrayList; 19 | 20 | public class ImageListRecyclerAdapter extends RecyclerView.Adapter{ 21 | 22 | private ArrayList mImages = new ArrayList<>(); 23 | private ImageListRecyclerClickListener mImageListRecyclerClickListener; 24 | private Context mContext; 25 | 26 | public ImageListRecyclerAdapter(Context context, ArrayList images, ImageListRecyclerClickListener imageListRecyclerClickListener) { 27 | mContext = context; 28 | mImages = images; 29 | mImageListRecyclerClickListener = imageListRecyclerClickListener; 30 | } 31 | 32 | @NonNull 33 | @Override 34 | public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 35 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_image_list_item, parent, false); 36 | final ViewHolder holder = new ViewHolder(view, mImageListRecyclerClickListener); 37 | return holder; 38 | } 39 | 40 | @Override 41 | public void onBindViewHolder(@NonNull ViewHolder holder, int position) { 42 | 43 | RequestOptions requestOptions = new RequestOptions() 44 | .placeholder(R.drawable.cwm_logo) 45 | .error(R.drawable.cwm_logo); 46 | 47 | Glide.with(mContext) 48 | .setDefaultRequestOptions(requestOptions) 49 | .load(mImages.get(position)) 50 | .into(((ViewHolder)holder).image); 51 | } 52 | 53 | @Override 54 | public int getItemCount() { 55 | return mImages.size(); 56 | } 57 | 58 | public class ViewHolder extends RecyclerView.ViewHolder implements 59 | View.OnClickListener 60 | { 61 | ImageView image; 62 | ImageListRecyclerClickListener mClickListener; 63 | 64 | public ViewHolder(View itemView, ImageListRecyclerClickListener clickListener) { 65 | super(itemView); 66 | image = itemView.findViewById(R.id.image); 67 | mClickListener = clickListener; 68 | itemView.setOnClickListener(this); 69 | } 70 | 71 | @Override 72 | public void onClick(View v) { 73 | mClickListener.onImageSelected(getAdapterPosition()); 74 | } 75 | } 76 | 77 | public interface ImageListRecyclerClickListener{ 78 | void onImageSelected(int position); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /app/src/main/java/com/codingwithmitch/googledirectionstest/adapters/UserRecyclerAdapter.java: -------------------------------------------------------------------------------- 1 | package com.codingwithmitch.googledirectionstest.adapters; 2 | 3 | import android.support.annotation.NonNull; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.TextView; 9 | 10 | import com.codingwithmitch.googledirectionstest.R; 11 | import com.codingwithmitch.googledirectionstest.models.Chatroom; 12 | import com.codingwithmitch.googledirectionstest.models.User; 13 | 14 | import java.util.ArrayList; 15 | 16 | public class UserRecyclerAdapter extends RecyclerView.Adapter{ 17 | 18 | private ArrayList mUsers = new ArrayList<>(); 19 | private UserListRecyclerClickListener mUserListRecyclerClickListener; 20 | 21 | public UserRecyclerAdapter(ArrayList mUsers, UserListRecyclerClickListener mUserListRecyclerClickListener) { 22 | this.mUsers = mUsers; 23 | this.mUserListRecyclerClickListener = mUserListRecyclerClickListener; 24 | } 25 | 26 | @NonNull 27 | @Override 28 | public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 29 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_user_list_item, parent, false); 30 | final ViewHolder holder = new ViewHolder(view, mUserListRecyclerClickListener); 31 | return holder; 32 | } 33 | 34 | @Override 35 | public void onBindViewHolder(@NonNull ViewHolder holder, int position) { 36 | 37 | ((ViewHolder)holder).username.setText(mUsers.get(position).getUsername()); 38 | ((ViewHolder)holder).email.setText(mUsers.get(position).getEmail()); 39 | } 40 | 41 | @Override 42 | public int getItemCount() { 43 | return mUsers.size(); 44 | } 45 | 46 | public class ViewHolder extends RecyclerView.ViewHolder implements 47 | View.OnClickListener 48 | { 49 | TextView username, email; 50 | UserListRecyclerClickListener mClickListener; 51 | 52 | public ViewHolder(View itemView, UserListRecyclerClickListener clickListener) { 53 | super(itemView); 54 | username = itemView.findViewById(R.id.username); 55 | email = itemView.findViewById(R.id.email); 56 | mClickListener = clickListener; 57 | itemView.setOnClickListener(this); 58 | } 59 | 60 | @Override 61 | public void onClick(View v) { 62 | mClickListener.onUserSelected(getAdapterPosition()); 63 | } 64 | } 65 | 66 | public interface UserListRecyclerClickListener{ 67 | void onUserSelected(int position); 68 | } 69 | 70 | } 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /app/src/main/java/com/codingwithmitch/googledirectionstest/models/ChatMessage.java: -------------------------------------------------------------------------------- 1 | package com.codingwithmitch.googledirectionstest.models; 2 | 3 | import com.google.firebase.firestore.ServerTimestamp; 4 | 5 | import java.util.Date; 6 | 7 | public class ChatMessage { 8 | 9 | private User user; 10 | private String message; 11 | private String message_id; 12 | private @ServerTimestamp Date timestamp; 13 | 14 | public ChatMessage(User user, String message, String message_id, Date timestamp) { 15 | this.user = user; 16 | this.message = message; 17 | this.message_id = message_id; 18 | this.timestamp = timestamp; 19 | } 20 | 21 | public ChatMessage() { 22 | 23 | } 24 | 25 | public User getUser() { 26 | return user; 27 | } 28 | 29 | public void setUser(User user) { 30 | this.user = user; 31 | } 32 | 33 | public String getMessage() { 34 | return message; 35 | } 36 | 37 | public void setMessage(String message) { 38 | this.message = message; 39 | } 40 | 41 | public String getMessage_id() { 42 | return message_id; 43 | } 44 | 45 | public void setMessage_id(String message_id) { 46 | this.message_id = message_id; 47 | } 48 | 49 | public Date getTimestamp() { 50 | return timestamp; 51 | } 52 | 53 | public void setTimestamp(Date timestamp) { 54 | this.timestamp = timestamp; 55 | } 56 | 57 | @Override 58 | public String toString() { 59 | return "ChatMessage{" + 60 | "user=" + user + 61 | ", message='" + message + '\'' + 62 | ", message_id='" + message_id + '\'' + 63 | ", timestamp=" + timestamp + 64 | '}'; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /app/src/main/java/com/codingwithmitch/googledirectionstest/models/Chatroom.java: -------------------------------------------------------------------------------- 1 | package com.codingwithmitch.googledirectionstest.models; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | public class Chatroom implements Parcelable { 7 | 8 | private String title; 9 | private String chatroom_id; 10 | 11 | 12 | public Chatroom(String title, String chatroom_id) { 13 | this.title = title; 14 | this.chatroom_id = chatroom_id; 15 | } 16 | 17 | public Chatroom() { 18 | 19 | } 20 | 21 | protected Chatroom(Parcel in) { 22 | title = in.readString(); 23 | chatroom_id = in.readString(); 24 | } 25 | 26 | public static final Creator CREATOR = new Creator() { 27 | @Override 28 | public Chatroom createFromParcel(Parcel in) { 29 | return new Chatroom(in); 30 | } 31 | 32 | @Override 33 | public Chatroom[] newArray(int size) { 34 | return new Chatroom[size]; 35 | } 36 | }; 37 | 38 | public String getTitle() { 39 | return title; 40 | } 41 | 42 | public void setTitle(String title) { 43 | this.title = title; 44 | } 45 | 46 | public String getChatroom_id() { 47 | return chatroom_id; 48 | } 49 | 50 | public void setChatroom_id(String chatroom_id) { 51 | this.chatroom_id = chatroom_id; 52 | } 53 | 54 | @Override 55 | public String toString() { 56 | return "Chatroom{" + 57 | "title='" + title + '\'' + 58 | ", chatroom_id='" + chatroom_id + '\'' + 59 | '}'; 60 | } 61 | 62 | @Override 63 | public int describeContents() { 64 | return 0; 65 | } 66 | 67 | @Override 68 | public void writeToParcel(Parcel dest, int flags) { 69 | dest.writeString(title); 70 | dest.writeString(chatroom_id); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/com/codingwithmitch/googledirectionstest/models/ClusterMarker.java: -------------------------------------------------------------------------------- 1 | package com.codingwithmitch.googledirectionstest.models; 2 | 3 | import com.google.android.gms.maps.model.LatLng; 4 | import com.google.maps.android.clustering.ClusterItem; 5 | 6 | public class ClusterMarker implements ClusterItem { 7 | 8 | private LatLng position; 9 | private String title; 10 | private String snippet; 11 | private int iconPicture; 12 | private User user; 13 | 14 | public ClusterMarker(LatLng position, String title, String snippet, int iconPicture, User user) { 15 | this.position = position; 16 | this.title = title; 17 | this.snippet = snippet; 18 | this.iconPicture = iconPicture; 19 | this.user = user; 20 | } 21 | 22 | public int getIconPicture() { 23 | return iconPicture; 24 | } 25 | 26 | public void setIconPicture(int iconPicture) { 27 | this.iconPicture = iconPicture; 28 | } 29 | 30 | public User getUser() { 31 | return user; 32 | } 33 | 34 | public void setUser(User user) { 35 | this.user = user; 36 | } 37 | 38 | public void setPosition(LatLng position) { 39 | this.position = position; 40 | } 41 | 42 | public void setTitle(String title) { 43 | this.title = title; 44 | } 45 | 46 | public void setSnippet(String snippet) { 47 | this.snippet = snippet; 48 | } 49 | 50 | public LatLng getPosition() { 51 | return position; 52 | } 53 | 54 | public String getTitle() { 55 | return title; 56 | } 57 | 58 | public String getSnippet() { 59 | return snippet; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/codingwithmitch/googledirectionstest/models/PolylineData.java: -------------------------------------------------------------------------------- 1 | package com.codingwithmitch.googledirectionstest.models; 2 | 3 | import com.google.android.gms.maps.model.Polyline; 4 | import com.google.maps.model.DirectionsLeg; 5 | 6 | public class PolylineData { 7 | 8 | private Polyline polyline; 9 | private DirectionsLeg leg; 10 | 11 | public PolylineData(Polyline polyline, DirectionsLeg leg) { 12 | this.polyline = polyline; 13 | this.leg = leg; 14 | } 15 | 16 | public Polyline getPolyline() { 17 | return polyline; 18 | } 19 | 20 | public void setPolyline(Polyline polyline) { 21 | this.polyline = polyline; 22 | } 23 | 24 | public DirectionsLeg getLeg() { 25 | return leg; 26 | } 27 | 28 | public void setLeg(DirectionsLeg leg) { 29 | this.leg = leg; 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return "PolylineData{" + 35 | "polyline=" + polyline + 36 | ", leg=" + leg + 37 | '}'; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/codingwithmitch/googledirectionstest/models/User.java: -------------------------------------------------------------------------------- 1 | package com.codingwithmitch.googledirectionstest.models; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | public class User implements Parcelable{ 7 | 8 | private String email; 9 | private String user_id; 10 | private String username; 11 | private String avatar; 12 | 13 | public User(String email, String user_id, String username, String avatar) { 14 | this.email = email; 15 | this.user_id = user_id; 16 | this.username = username; 17 | this.avatar = avatar; 18 | } 19 | 20 | public User() { 21 | 22 | } 23 | 24 | protected User(Parcel in) { 25 | email = in.readString(); 26 | user_id = in.readString(); 27 | username = in.readString(); 28 | avatar = in.readString(); 29 | } 30 | 31 | public static final Creator CREATOR = new Creator() { 32 | @Override 33 | public User createFromParcel(Parcel in) { 34 | return new User(in); 35 | } 36 | 37 | @Override 38 | public User[] newArray(int size) { 39 | return new User[size]; 40 | } 41 | }; 42 | 43 | public String getAvatar() { 44 | return avatar; 45 | } 46 | 47 | public void setAvatar(String avatar) { 48 | this.avatar = avatar; 49 | } 50 | 51 | public static Creator getCREATOR() { 52 | return CREATOR; 53 | } 54 | 55 | public String getEmail() { 56 | return email; 57 | } 58 | 59 | public void setEmail(String email) { 60 | this.email = email; 61 | } 62 | 63 | public String getUser_id() { 64 | return user_id; 65 | } 66 | 67 | public void setUser_id(String user_id) { 68 | this.user_id = user_id; 69 | } 70 | 71 | public String getUsername() { 72 | return username; 73 | } 74 | 75 | public void setUsername(String username) { 76 | this.username = username; 77 | } 78 | 79 | @Override 80 | public String toString() { 81 | return "User{" + 82 | "email='" + email + '\'' + 83 | ", user_id='" + user_id + '\'' + 84 | ", username='" + username + '\'' + 85 | ", avatar='" + avatar + '\'' + 86 | '}'; 87 | } 88 | 89 | @Override 90 | public int describeContents() { 91 | return 0; 92 | } 93 | 94 | @Override 95 | public void writeToParcel(Parcel dest, int flags) { 96 | dest.writeString(email); 97 | dest.writeString(user_id); 98 | dest.writeString(username); 99 | dest.writeString(avatar); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /app/src/main/java/com/codingwithmitch/googledirectionstest/models/UserLocation.java: -------------------------------------------------------------------------------- 1 | package com.codingwithmitch.googledirectionstest.models; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.google.firebase.firestore.GeoPoint; 7 | import com.google.firebase.firestore.ServerTimestamp; 8 | 9 | import java.util.Date; 10 | 11 | public class UserLocation implements Parcelable { 12 | 13 | private User user; 14 | private GeoPoint geo_point; 15 | private @ServerTimestamp Date timestamp; 16 | 17 | public UserLocation(User user, GeoPoint geo_point, Date timestamp) { 18 | this.user = user; 19 | this.geo_point = geo_point; 20 | this.timestamp = timestamp; 21 | } 22 | 23 | public UserLocation() { 24 | 25 | } 26 | 27 | protected UserLocation(Parcel in) { 28 | user = in.readParcelable(User.class.getClassLoader()); 29 | } 30 | 31 | public static final Creator CREATOR = new Creator() { 32 | @Override 33 | public UserLocation createFromParcel(Parcel in) { 34 | return new UserLocation(in); 35 | } 36 | 37 | @Override 38 | public UserLocation[] newArray(int size) { 39 | return new UserLocation[size]; 40 | } 41 | }; 42 | 43 | public User getUser() { 44 | return user; 45 | } 46 | 47 | public void setUser(User user) { 48 | this.user = user; 49 | } 50 | 51 | public GeoPoint getGeo_point() { 52 | return geo_point; 53 | } 54 | 55 | public void setGeo_point(GeoPoint geo_point) { 56 | this.geo_point = geo_point; 57 | } 58 | 59 | public Date getTimestamp() { 60 | return timestamp; 61 | } 62 | 63 | public void setTimestamp(Date timestamp) { 64 | this.timestamp = timestamp; 65 | } 66 | 67 | @Override 68 | public String toString() { 69 | return "UserLocation{" + 70 | "user=" + user + 71 | ", geo_point=" + geo_point + 72 | ", timestamp=" + timestamp + 73 | '}'; 74 | } 75 | 76 | @Override 77 | public int describeContents() { 78 | return 0; 79 | } 80 | 81 | @Override 82 | public void writeToParcel(Parcel dest, int flags) { 83 | dest.writeParcelable(user, flags); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /app/src/main/java/com/codingwithmitch/googledirectionstest/models/UserMarker.java: -------------------------------------------------------------------------------- 1 | package com.codingwithmitch.googledirectionstest.models; 2 | 3 | import com.google.android.gms.maps.model.Marker; 4 | 5 | public class UserMarker { 6 | 7 | private Marker marker; 8 | private User user; 9 | 10 | 11 | public UserMarker(Marker marker, User user) { 12 | this.marker = marker; 13 | this.user = user; 14 | } 15 | 16 | public UserMarker() { 17 | } 18 | 19 | 20 | public Marker getMarker() { 21 | return marker; 22 | } 23 | 24 | public void setMarker(Marker marker) { 25 | this.marker = marker; 26 | } 27 | 28 | public User getUser() { 29 | return user; 30 | } 31 | 32 | @Override 33 | public String toString() { 34 | return "UserMarker{" + 35 | "marker=" + marker + 36 | ", user=" + user + 37 | '}'; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/codingwithmitch/googledirectionstest/services/LocationService.java: -------------------------------------------------------------------------------- 1 | package com.codingwithmitch.googledirectionstest.services; 2 | 3 | 4 | import android.Manifest; 5 | import android.app.Notification; 6 | import android.app.NotificationChannel; 7 | import android.app.NotificationManager; 8 | import android.app.Service; 9 | import android.content.Context; 10 | import android.content.Intent; 11 | import android.content.pm.PackageManager; 12 | import android.location.Location; 13 | import android.location.LocationListener; 14 | import android.location.LocationManager; 15 | import android.os.Build; 16 | import android.os.Bundle; 17 | import android.os.Handler; 18 | import android.os.IBinder; 19 | import android.os.Looper; 20 | import android.support.annotation.NonNull; 21 | import android.support.annotation.Nullable; 22 | import android.support.annotation.RequiresApi; 23 | import android.support.v4.app.ActivityCompat; 24 | import android.support.v4.app.NotificationCompat; 25 | import android.util.Log; 26 | import android.widget.Toast; 27 | 28 | import com.codingwithmitch.googledirectionstest.R; 29 | import com.codingwithmitch.googledirectionstest.UserClient; 30 | import com.codingwithmitch.googledirectionstest.models.User; 31 | import com.codingwithmitch.googledirectionstest.models.UserLocation; 32 | import com.google.android.gms.location.FusedLocationProviderClient; 33 | import com.google.android.gms.location.LocationCallback; 34 | import com.google.android.gms.location.LocationRequest; 35 | import com.google.android.gms.location.LocationResult; 36 | import com.google.android.gms.location.LocationServices; 37 | import com.google.android.gms.location.LocationSettingsRequest; 38 | import com.google.android.gms.location.SettingsClient; 39 | import com.google.android.gms.tasks.OnCompleteListener; 40 | import com.google.android.gms.tasks.Task; 41 | import com.google.firebase.auth.FirebaseAuth; 42 | import com.google.firebase.firestore.DocumentReference; 43 | import com.google.firebase.firestore.FirebaseFirestore; 44 | import com.google.firebase.firestore.GeoPoint; 45 | 46 | import java.util.HashMap; 47 | 48 | 49 | 50 | public class LocationService extends Service { 51 | 52 | private static final String TAG = "LocationService"; 53 | 54 | private FusedLocationProviderClient mFusedLocationClient; 55 | private final static long UPDATE_INTERVAL = 4 * 1000; /* 4 secs */ 56 | private final static long FASTEST_INTERVAL = 2000; /* 2 sec */ 57 | 58 | @Nullable 59 | @Override 60 | public IBinder onBind(Intent intent) { 61 | return null; 62 | } 63 | 64 | @Override 65 | public void onCreate() { 66 | super.onCreate(); 67 | 68 | mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this); 69 | 70 | if (Build.VERSION.SDK_INT >= 26) { 71 | String CHANNEL_ID = "my_channel_01"; 72 | NotificationChannel channel = new NotificationChannel(CHANNEL_ID, 73 | "My Channel", 74 | NotificationManager.IMPORTANCE_DEFAULT); 75 | 76 | ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel); 77 | 78 | Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID) 79 | .setContentTitle("") 80 | .setContentText("").build(); 81 | 82 | startForeground(1, notification); 83 | } 84 | } 85 | 86 | @Override 87 | public int onStartCommand(Intent intent, int flags, int startId) { 88 | Log.d(TAG, "onStartCommand: called."); 89 | getLocation(); 90 | return START_NOT_STICKY; 91 | } 92 | 93 | @Override 94 | public void onDestroy() { 95 | super.onDestroy(); 96 | Log.e(TAG, "onDestroy: called"); 97 | 98 | } 99 | 100 | 101 | 102 | private void getLocation() { 103 | 104 | // ---------------------------------- LocationRequest ------------------------------------ 105 | // Create the location request to start receiving updates 106 | LocationRequest mLocationRequest = new LocationRequest(); 107 | // mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY); 108 | mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); 109 | // mLocationRequest.setPriority(LocationRequest.PRIORITY_LOW_POWER); 110 | // mLocationRequest.setPriority(LocationRequest.PRIORITY_NO_POWER); 111 | mLocationRequest.setInterval(UPDATE_INTERVAL); 112 | mLocationRequest.setFastestInterval(FASTEST_INTERVAL); 113 | 114 | // Create LocationSettingsRequest object using location request 115 | LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder(); 116 | builder.addLocationRequest(mLocationRequest); 117 | LocationSettingsRequest locationSettingsRequest = builder.build(); 118 | 119 | // Check whether location settings are satisfied 120 | // https://developers.google.com/android/reference/com/google/android/gms/location/SettingsClient 121 | SettingsClient settingsClient = LocationServices.getSettingsClient(this); 122 | settingsClient.checkLocationSettings(locationSettingsRequest); 123 | 124 | // new Google API SDK v11 uses getFusedLocationProviderClient(this) 125 | if (ActivityCompat.checkSelfPermission(this, 126 | Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED 127 | && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) 128 | != PackageManager.PERMISSION_GRANTED) { 129 | Log.d(TAG, "getLocation: stopping the location service."); 130 | stopSelf(); 131 | return; 132 | } 133 | Log.d(TAG, "getLocation: getting location information."); 134 | mFusedLocationClient.requestLocationUpdates(mLocationRequest, new LocationCallback() { 135 | @Override 136 | public void onLocationResult(LocationResult locationResult) { 137 | 138 | Log.d(TAG, "onLocationResult: got location result."); 139 | 140 | Location location = locationResult.getLastLocation(); 141 | 142 | if (location != null) { 143 | User user = ((UserClient)(getApplicationContext())).getUser(); 144 | GeoPoint geoPoint = new GeoPoint(location.getLatitude(), location.getLongitude()); 145 | UserLocation userLocation = new UserLocation(user, geoPoint, null); 146 | saveUserLocation(userLocation); 147 | } 148 | } 149 | }, 150 | Looper.myLooper()); 151 | } 152 | 153 | private void saveUserLocation(final UserLocation userLocation){ 154 | 155 | try{ 156 | DocumentReference locationRef = FirebaseFirestore.getInstance() 157 | .collection(getString(R.string.collection_user_locations)) 158 | .document(FirebaseAuth.getInstance().getUid()); 159 | 160 | locationRef.set(userLocation).addOnCompleteListener(new OnCompleteListener() { 161 | @Override 162 | public void onComplete(@NonNull Task task) { 163 | if(task.isSuccessful()){ 164 | Log.d(TAG, "onComplete: \ninserted user location into database." + 165 | "\n latitude: " + userLocation.getGeo_point().getLatitude() + 166 | "\n longitude: " + userLocation.getGeo_point().getLongitude()); 167 | } 168 | } 169 | }); 170 | }catch (NullPointerException e){ 171 | Log.e(TAG, "saveUserLocation: User instance is null, stopping location service."); 172 | Log.e(TAG, "saveUserLocation: NullPointerException: " + e.getMessage() ); 173 | stopSelf(); 174 | } 175 | 176 | } 177 | 178 | 179 | 180 | 181 | } -------------------------------------------------------------------------------- /app/src/main/java/com/codingwithmitch/googledirectionstest/ui/ChatroomActivity.java: -------------------------------------------------------------------------------- 1 | package com.codingwithmitch.googledirectionstest.ui; 2 | 3 | import android.os.Build; 4 | import android.os.Bundle; 5 | import android.support.annotation.NonNull; 6 | import android.support.annotation.Nullable; 7 | import android.support.design.widget.Snackbar; 8 | import android.support.v4.app.FragmentTransaction; 9 | import android.support.v7.app.AppCompatActivity; 10 | import android.support.v7.widget.LinearLayoutManager; 11 | import android.support.v7.widget.RecyclerView; 12 | import android.util.Log; 13 | import android.view.Menu; 14 | import android.view.MenuItem; 15 | import android.view.View; 16 | import android.widget.EditText; 17 | import android.widget.TextView; 18 | 19 | import com.codingwithmitch.googledirectionstest.R; 20 | import com.codingwithmitch.googledirectionstest.UserClient; 21 | import com.codingwithmitch.googledirectionstest.adapters.ChatMessageRecyclerAdapter; 22 | import com.codingwithmitch.googledirectionstest.adapters.ChatroomRecyclerAdapter; 23 | import com.codingwithmitch.googledirectionstest.models.ChatMessage; 24 | import com.codingwithmitch.googledirectionstest.models.Chatroom; 25 | import com.codingwithmitch.googledirectionstest.models.User; 26 | import com.codingwithmitch.googledirectionstest.models.UserLocation; 27 | import com.google.android.gms.tasks.OnCompleteListener; 28 | import com.google.android.gms.tasks.Task; 29 | import com.google.firebase.auth.FirebaseAuth; 30 | import com.google.firebase.firestore.CollectionReference; 31 | import com.google.firebase.firestore.DocumentReference; 32 | import com.google.firebase.firestore.DocumentSnapshot; 33 | import com.google.firebase.firestore.EventListener; 34 | import com.google.firebase.firestore.FirebaseFirestore; 35 | import com.google.firebase.firestore.FirebaseFirestoreException; 36 | import com.google.firebase.firestore.ListenerRegistration; 37 | import com.google.firebase.firestore.Query; 38 | import com.google.firebase.firestore.QueryDocumentSnapshot; 39 | import com.google.firebase.firestore.QuerySnapshot; 40 | 41 | import java.util.ArrayList; 42 | import java.util.HashSet; 43 | import java.util.Set; 44 | 45 | public class ChatroomActivity extends AppCompatActivity implements 46 | View.OnClickListener 47 | { 48 | 49 | private static final String TAG = "ChatroomActivity"; 50 | 51 | //widgets 52 | private Chatroom mChatroom; 53 | private EditText mMessage; 54 | 55 | //vars 56 | private ListenerRegistration mChatMessageEventListener, mUserListEventListener; 57 | private RecyclerView mChatMessageRecyclerView; 58 | private ChatMessageRecyclerAdapter mChatMessageRecyclerAdapter; 59 | private FirebaseFirestore mDb; 60 | private ArrayList mMessages = new ArrayList<>(); 61 | private Set mMessageIds = new HashSet<>(); 62 | private ArrayList mUserList = new ArrayList<>(); 63 | private ArrayList mUserLocations = new ArrayList<>(); 64 | private UserListFragment mUserListFragment; 65 | 66 | 67 | @Override 68 | protected void onCreate(@Nullable Bundle savedInstanceState) { 69 | super.onCreate(savedInstanceState); 70 | setContentView(R.layout.activity_chatroom); 71 | mMessage = findViewById(R.id.input_message); 72 | mChatMessageRecyclerView = findViewById(R.id.chatmessage_recycler_view); 73 | 74 | findViewById(R.id.checkmark).setOnClickListener(this); 75 | 76 | mDb = FirebaseFirestore.getInstance(); 77 | 78 | getIncomingIntent(); 79 | initChatroomRecyclerView(); 80 | getChatroomUsers(); 81 | } 82 | 83 | private void getChatMessages(){ 84 | 85 | CollectionReference messagesRef = mDb 86 | .collection(getString(R.string.collection_chatrooms)) 87 | .document(mChatroom.getChatroom_id()) 88 | .collection(getString(R.string.collection_chat_messages)); 89 | 90 | mChatMessageEventListener = messagesRef 91 | .orderBy("timestamp", Query.Direction.ASCENDING) 92 | .addSnapshotListener(new EventListener() { 93 | @Override 94 | public void onEvent(@javax.annotation.Nullable QuerySnapshot queryDocumentSnapshots, @javax.annotation.Nullable FirebaseFirestoreException e) { 95 | if (e != null) { 96 | Log.e(TAG, "onEvent: Listen failed.", e); 97 | return; 98 | } 99 | 100 | if(queryDocumentSnapshots != null){ 101 | for (QueryDocumentSnapshot doc : queryDocumentSnapshots) { 102 | 103 | ChatMessage message = doc.toObject(ChatMessage.class); 104 | if(!mMessageIds.contains(message.getMessage_id())){ 105 | mMessageIds.add(message.getMessage_id()); 106 | mMessages.add(message); 107 | mChatMessageRecyclerView.smoothScrollToPosition(mMessages.size() - 1); 108 | } 109 | 110 | } 111 | mChatMessageRecyclerAdapter.notifyDataSetChanged(); 112 | 113 | } 114 | } 115 | }); 116 | } 117 | 118 | private void getUserLocation(User user){ 119 | DocumentReference locationsRef = mDb 120 | .collection(getString(R.string.collection_user_locations)) 121 | .document(user.getUser_id()); 122 | 123 | locationsRef.get().addOnCompleteListener(new OnCompleteListener() { 124 | @Override 125 | public void onComplete(@NonNull Task task) { 126 | 127 | if(task.isSuccessful()){ 128 | if(task.getResult().toObject(UserLocation.class) != null){ 129 | 130 | mUserLocations.add(task.getResult().toObject(UserLocation.class)); 131 | } 132 | } 133 | } 134 | }); 135 | 136 | } 137 | 138 | 139 | private void getChatroomUsers(){ 140 | 141 | CollectionReference usersRef = mDb 142 | .collection(getString(R.string.collection_chatrooms)) 143 | .document(mChatroom.getChatroom_id()) 144 | .collection(getString(R.string.collection_chatroom_user_list)); 145 | 146 | mUserListEventListener = usersRef 147 | .addSnapshotListener(new EventListener() { 148 | @Override 149 | public void onEvent(@javax.annotation.Nullable QuerySnapshot queryDocumentSnapshots, @javax.annotation.Nullable FirebaseFirestoreException e) { 150 | if (e != null) { 151 | Log.e(TAG, "onEvent: Listen failed.", e); 152 | return; 153 | } 154 | 155 | if(queryDocumentSnapshots != null){ 156 | 157 | // Clear the list and add all the users again 158 | mUserList.clear(); 159 | mUserList = new ArrayList<>(); 160 | 161 | // Clear the user locations list 162 | mUserLocations.clear(); 163 | mUserLocations = new ArrayList<>(); 164 | 165 | for (QueryDocumentSnapshot doc : queryDocumentSnapshots) { 166 | User user = doc.toObject(User.class); 167 | mUserList.add(user); 168 | getUserLocation(user); 169 | } 170 | 171 | Log.d(TAG, "onEvent: user list size: " + mUserList.size()); 172 | } 173 | } 174 | }); 175 | } 176 | 177 | private void initChatroomRecyclerView(){ 178 | mChatMessageRecyclerAdapter = new ChatMessageRecyclerAdapter(mMessages, new ArrayList(), this); 179 | mChatMessageRecyclerView.setAdapter(mChatMessageRecyclerAdapter); 180 | mChatMessageRecyclerView.setLayoutManager(new LinearLayoutManager(this)); 181 | 182 | mChatMessageRecyclerView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { 183 | @Override 184 | public void onLayoutChange(View v, 185 | int left, int top, int right, int bottom, 186 | int oldLeft, int oldTop, int oldRight, int oldBottom) { 187 | if (bottom < oldBottom) { 188 | mChatMessageRecyclerView.postDelayed(new Runnable() { 189 | @Override 190 | public void run() { 191 | if(mMessages.size() > 0){ 192 | mChatMessageRecyclerView.smoothScrollToPosition( 193 | mChatMessageRecyclerView.getAdapter().getItemCount() - 1); 194 | } 195 | 196 | } 197 | }, 100); 198 | } 199 | } 200 | }); 201 | 202 | } 203 | 204 | 205 | private void insertNewMessage(){ 206 | String message = mMessage.getText().toString(); 207 | 208 | if(!message.equals("")){ 209 | message = message.replaceAll(System.getProperty("line.separator"), ""); 210 | 211 | DocumentReference newMessageDoc = mDb 212 | .collection(getString(R.string.collection_chatrooms)) 213 | .document(mChatroom.getChatroom_id()) 214 | .collection(getString(R.string.collection_chat_messages)) 215 | .document(); 216 | 217 | ChatMessage newChatMessage = new ChatMessage(); 218 | newChatMessage.setMessage(message); 219 | newChatMessage.setMessage_id(newMessageDoc.getId()); 220 | 221 | User user = ((UserClient)(getApplicationContext())).getUser(); 222 | Log.d(TAG, "insertNewMessage: retrieved user client: " + user.toString()); 223 | newChatMessage.setUser(user); 224 | 225 | newMessageDoc.set(newChatMessage).addOnCompleteListener(new OnCompleteListener() { 226 | @Override 227 | public void onComplete(@NonNull Task task) { 228 | if(task.isSuccessful()){ 229 | clearMessage(); 230 | }else{ 231 | View parentLayout = findViewById(android.R.id.content); 232 | Snackbar.make(parentLayout, "Something went wrong.", Snackbar.LENGTH_SHORT).show(); 233 | } 234 | } 235 | }); 236 | } 237 | } 238 | 239 | private void clearMessage(){ 240 | mMessage.setText(""); 241 | } 242 | 243 | private void inflateUserListFragment(){ 244 | UserListFragment fragment = UserListFragment.newInstance(); 245 | Bundle bundle = new Bundle(); 246 | bundle.putParcelableArrayList(getString(R.string.intent_user_list), mUserList); 247 | bundle.putParcelableArrayList(getString(R.string.intent_user_locations), mUserLocations); 248 | fragment.setArguments(bundle); 249 | 250 | FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); 251 | transaction.replace(R.id.user_list_container, fragment, getString(R.string.fragment_user_list)); 252 | transaction.addToBackStack(getString(R.string.fragment_user_list)); 253 | transaction.commit(); 254 | } 255 | 256 | 257 | private void getIncomingIntent(){ 258 | if(getIntent().hasExtra(getString(R.string.intent_chatroom))){ 259 | mChatroom = getIntent().getParcelableExtra(getString(R.string.intent_chatroom)); 260 | setChatroomName(); 261 | joinChatroom(); 262 | } 263 | } 264 | 265 | private void leaveChatroom(){ 266 | 267 | DocumentReference joinChatroomRef = mDb 268 | .collection(getString(R.string.collection_chatrooms)) 269 | .document(mChatroom.getChatroom_id()) 270 | .collection(getString(R.string.collection_chatroom_user_list)) 271 | .document(FirebaseAuth.getInstance().getUid()); 272 | 273 | joinChatroomRef.delete(); 274 | } 275 | 276 | private void joinChatroom(){ 277 | 278 | DocumentReference joinChatroomRef = mDb 279 | .collection(getString(R.string.collection_chatrooms)) 280 | .document(mChatroom.getChatroom_id()) 281 | .collection(getString(R.string.collection_chatroom_user_list)) 282 | .document(FirebaseAuth.getInstance().getUid()); 283 | 284 | User user = ((UserClient)(getApplicationContext())).getUser(); 285 | joinChatroomRef.set(user); // Don't care about listening for completion. 286 | } 287 | 288 | private void setChatroomName(){ 289 | getSupportActionBar().setTitle(mChatroom.getTitle()); 290 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 291 | getSupportActionBar().setHomeButtonEnabled(true); 292 | } 293 | 294 | @Override 295 | protected void onResume() { 296 | super.onResume(); 297 | getChatMessages(); 298 | } 299 | 300 | 301 | @Override 302 | protected void onDestroy() { 303 | super.onDestroy(); 304 | if(mChatMessageEventListener != null){ 305 | mChatMessageEventListener.remove(); 306 | } 307 | if(mUserListEventListener != null){ 308 | mUserListEventListener.remove(); 309 | } 310 | } 311 | 312 | @Override 313 | public boolean onCreateOptionsMenu(Menu menu) { 314 | getMenuInflater().inflate(R.menu.chatroom_menu, menu); 315 | return super.onCreateOptionsMenu(menu); 316 | } 317 | 318 | @Override 319 | public boolean onOptionsItemSelected(MenuItem item) { 320 | switch(item.getItemId()){ 321 | case android.R.id.home:{ 322 | UserListFragment fragment = 323 | (UserListFragment) getSupportFragmentManager().findFragmentByTag(getString(R.string.fragment_user_list)); 324 | if(fragment != null){ 325 | if(fragment.isVisible()){ 326 | getSupportFragmentManager().popBackStack(); 327 | return true; 328 | } 329 | } 330 | finish(); 331 | return true; 332 | } 333 | case R.id.action_chatroom_user_list:{ 334 | inflateUserListFragment(); 335 | return true; 336 | } 337 | case R.id.action_chatroom_leave:{ 338 | leaveChatroom(); 339 | return true; 340 | } 341 | default:{ 342 | return super.onOptionsItemSelected(item); 343 | } 344 | } 345 | 346 | } 347 | 348 | @Override 349 | public void onClick(View v) { 350 | switch (v.getId()){ 351 | case R.id.checkmark:{ 352 | insertNewMessage(); 353 | } 354 | } 355 | } 356 | 357 | } 358 | -------------------------------------------------------------------------------- /app/src/main/java/com/codingwithmitch/googledirectionstest/ui/IProfile.java: -------------------------------------------------------------------------------- 1 | package com.codingwithmitch.googledirectionstest.ui; 2 | 3 | public interface IProfile { 4 | 5 | void onImageSelected(int resource); 6 | } 7 | -------------------------------------------------------------------------------- /app/src/main/java/com/codingwithmitch/googledirectionstest/ui/ImageListFragment.java: -------------------------------------------------------------------------------- 1 | package com.codingwithmitch.googledirectionstest.ui; 2 | 3 | 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | import android.support.v4.app.Fragment; 7 | import android.support.v7.widget.LinearLayoutManager; 8 | import android.support.v7.widget.RecyclerView; 9 | import android.support.v7.widget.StaggeredGridLayoutManager; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.view.ViewGroup; 13 | 14 | import com.codingwithmitch.googledirectionstest.R; 15 | import com.codingwithmitch.googledirectionstest.adapters.ImageListRecyclerAdapter; 16 | 17 | import java.util.ArrayList; 18 | 19 | /** 20 | * A simple {@link Fragment} subclass. 21 | * Use the {@link ImageListFragment#newInstance} factory method to 22 | * create an instance of this fragment. 23 | */ 24 | public class ImageListFragment extends Fragment implements ImageListRecyclerAdapter.ImageListRecyclerClickListener{ 25 | 26 | private static final String TAG = "ImageListFragment"; 27 | private static final int NUM_COLUMNS = 2; 28 | 29 | //widgets 30 | private RecyclerView mRecyclerView; 31 | 32 | 33 | //vars 34 | private ArrayList mImageResources = new ArrayList<>(); 35 | private IProfile mIProfile; 36 | 37 | public ImageListFragment() { 38 | // Required empty public constructor 39 | } 40 | 41 | 42 | public static ImageListFragment newInstance() { 43 | return new ImageListFragment(); 44 | } 45 | 46 | @Override 47 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 48 | Bundle savedInstanceState) { 49 | View view = inflater.inflate(R.layout.fragment_image_list, container, false); 50 | mRecyclerView = view.findViewById(R.id.image_list_recyclerview); 51 | 52 | getImageResouces(); 53 | initRecyclerview(); 54 | 55 | return view; 56 | } 57 | 58 | private void getImageResouces(){ 59 | mImageResources.add(R.drawable.cwm_logo); 60 | mImageResources.add(R.drawable.cartman_cop); 61 | mImageResources.add(R.drawable.eric_cartman); 62 | mImageResources.add(R.drawable.ike); 63 | mImageResources.add(R.drawable.kyle); 64 | mImageResources.add(R.drawable.satan); 65 | mImageResources.add(R.drawable.chef); 66 | mImageResources.add(R.drawable.tweek); 67 | } 68 | 69 | private void initRecyclerview(){ 70 | ImageListRecyclerAdapter mAdapter = new ImageListRecyclerAdapter(getActivity(), mImageResources, this); 71 | StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(NUM_COLUMNS, LinearLayoutManager.VERTICAL); 72 | mRecyclerView.setLayoutManager(staggeredGridLayoutManager); 73 | mRecyclerView.setAdapter(mAdapter); 74 | } 75 | 76 | @Override 77 | public void onAttach(Context context) { 78 | super.onAttach(context); 79 | mIProfile = (IProfile) getActivity(); 80 | } 81 | 82 | @Override 83 | public void onImageSelected(int position) { 84 | mIProfile.onImageSelected(mImageResources.get(position)); 85 | } 86 | } 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /app/src/main/java/com/codingwithmitch/googledirectionstest/ui/LoginActivity.java: -------------------------------------------------------------------------------- 1 | package com.codingwithmitch.googledirectionstest.ui; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.annotation.NonNull; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.util.Log; 8 | import android.view.View; 9 | import android.view.WindowManager; 10 | import android.widget.Button; 11 | import android.widget.EditText; 12 | import android.widget.ProgressBar; 13 | import android.widget.TextView; 14 | import android.widget.Toast; 15 | 16 | import com.codingwithmitch.googledirectionstest.R; 17 | import com.codingwithmitch.googledirectionstest.UserClient; 18 | import com.codingwithmitch.googledirectionstest.models.User; 19 | import com.google.android.gms.tasks.OnCompleteListener; 20 | import com.google.android.gms.tasks.OnFailureListener; 21 | import com.google.android.gms.tasks.Task; 22 | import com.google.firebase.auth.AuthResult; 23 | import com.google.firebase.auth.FirebaseAuth; 24 | import com.google.firebase.auth.FirebaseUser; 25 | import com.google.firebase.firestore.DocumentReference; 26 | import com.google.firebase.firestore.DocumentSnapshot; 27 | import com.google.firebase.firestore.FirebaseFirestore; 28 | import com.google.firebase.firestore.FirebaseFirestoreSettings; 29 | 30 | import static android.text.TextUtils.isEmpty; 31 | 32 | public class LoginActivity extends AppCompatActivity implements 33 | View.OnClickListener 34 | { 35 | 36 | private static final String TAG = "LoginActivity"; 37 | 38 | //Firebase 39 | private FirebaseAuth.AuthStateListener mAuthListener; 40 | 41 | // widgets 42 | private EditText mEmail, mPassword; 43 | private ProgressBar mProgressBar; 44 | 45 | @Override 46 | protected void onCreate(Bundle savedInstanceState) { 47 | super.onCreate(savedInstanceState); 48 | setContentView(R.layout.activity_login); 49 | mEmail = findViewById(R.id.email); 50 | mPassword = findViewById(R.id.password); 51 | mProgressBar = findViewById(R.id.progressBar); 52 | 53 | setupFirebaseAuth(); 54 | findViewById(R.id.email_sign_in_button).setOnClickListener(this); 55 | findViewById(R.id.link_register).setOnClickListener(this); 56 | 57 | hideSoftKeyboard(); 58 | } 59 | 60 | 61 | 62 | 63 | private void showDialog(){ 64 | mProgressBar.setVisibility(View.VISIBLE); 65 | 66 | } 67 | 68 | private void hideDialog(){ 69 | if(mProgressBar.getVisibility() == View.VISIBLE){ 70 | mProgressBar.setVisibility(View.INVISIBLE); 71 | } 72 | } 73 | 74 | private void hideSoftKeyboard(){ 75 | this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); 76 | } 77 | 78 | /* 79 | ----------------------------- Firebase setup --------------------------------- 80 | */ 81 | private void setupFirebaseAuth(){ 82 | Log.d(TAG, "setupFirebaseAuth: started."); 83 | 84 | mAuthListener = new FirebaseAuth.AuthStateListener() { 85 | @Override 86 | public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) { 87 | FirebaseUser user = firebaseAuth.getCurrentUser(); 88 | if (user != null) { 89 | Log.d(TAG, "setupFirebaseAuth:signed_in:" + user.getUid()); 90 | Toast.makeText(LoginActivity.this, "Authenticated with: " + user.getEmail(), Toast.LENGTH_SHORT).show(); 91 | 92 | FirebaseFirestore db = FirebaseFirestore.getInstance(); 93 | FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder() 94 | .setTimestampsInSnapshotsEnabled(true) 95 | .build(); 96 | db.setFirestoreSettings(settings); 97 | 98 | DocumentReference userRef = db.collection(getString(R.string.collection_users)) 99 | .document(user.getUid()); 100 | 101 | userRef.get().addOnCompleteListener(new OnCompleteListener() { 102 | @Override 103 | public void onComplete(@NonNull Task task) { 104 | if(task.isSuccessful()){ 105 | Log.d(TAG, "setupFirebaseAuth: successfully set the user client."); 106 | User user = task.getResult().toObject(User.class); 107 | ((UserClient)(getApplicationContext())).setUser(user); 108 | } 109 | } 110 | }); 111 | 112 | Intent intent = new Intent(LoginActivity.this, MainActivity.class); 113 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 114 | startActivity(intent); 115 | finish(); 116 | 117 | } else { 118 | // User is signed out 119 | Log.d(TAG, "setupFirebaseAuth:signed_out"); 120 | } 121 | // ... 122 | } 123 | }; 124 | } 125 | 126 | @Override 127 | public void onStart() { 128 | super.onStart(); 129 | FirebaseAuth.getInstance().addAuthStateListener(mAuthListener); 130 | } 131 | 132 | @Override 133 | public void onStop() { 134 | super.onStop(); 135 | if (mAuthListener != null) { 136 | FirebaseAuth.getInstance().removeAuthStateListener(mAuthListener); 137 | } 138 | } 139 | 140 | private void signIn(){ 141 | //check if the fields are filled out 142 | if(!isEmpty(mEmail.getText().toString()) 143 | && !isEmpty(mPassword.getText().toString())){ 144 | Log.d(TAG, "onClick: attempting to authenticate."); 145 | 146 | showDialog(); 147 | 148 | FirebaseAuth.getInstance().signInWithEmailAndPassword(mEmail.getText().toString(), 149 | mPassword.getText().toString()) 150 | .addOnCompleteListener(new OnCompleteListener() { 151 | @Override 152 | public void onComplete(@NonNull Task task) { 153 | 154 | hideDialog(); 155 | 156 | } 157 | }).addOnFailureListener(new OnFailureListener() { 158 | @Override 159 | public void onFailure(@NonNull Exception e) { 160 | Toast.makeText(LoginActivity.this, "Authentication Failed", Toast.LENGTH_SHORT).show(); 161 | hideDialog(); 162 | } 163 | }); 164 | }else{ 165 | Toast.makeText(LoginActivity.this, "You didn't fill in all the fields.", Toast.LENGTH_SHORT).show(); 166 | } 167 | } 168 | 169 | @Override 170 | public void onClick(View view) { 171 | switch (view.getId()){ 172 | case R.id.link_register:{ 173 | Intent intent = new Intent(LoginActivity.this, RegisterActivity.class); 174 | startActivity(intent); 175 | break; 176 | } 177 | 178 | case R.id.email_sign_in_button:{ 179 | signIn(); 180 | break; 181 | } 182 | } 183 | } 184 | } -------------------------------------------------------------------------------- /app/src/main/java/com/codingwithmitch/googledirectionstest/ui/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.codingwithmitch.googledirectionstest.ui; 2 | 3 | import android.Manifest; 4 | import android.app.ActivityManager; 5 | import android.app.AlarmManager; 6 | import android.app.AlertDialog; 7 | import android.app.Dialog; 8 | import android.app.PendingIntent; 9 | import android.content.BroadcastReceiver; 10 | import android.content.Context; 11 | import android.content.DialogInterface; 12 | import android.content.Intent; 13 | import android.content.IntentFilter; 14 | import android.content.pm.PackageManager; 15 | 16 | import android.location.Location; 17 | import android.location.LocationManager; 18 | import android.os.Build; 19 | import android.os.Looper; 20 | import android.support.annotation.NonNull; 21 | import android.support.annotation.RequiresApi; 22 | import android.support.design.widget.Snackbar; 23 | import android.support.v4.app.ActivityCompat; 24 | import android.support.v4.content.ContextCompat; 25 | import android.support.v4.content.LocalBroadcastManager; 26 | import android.support.v7.app.AppCompatActivity; 27 | import android.os.Bundle; 28 | import android.support.v7.widget.LinearLayoutManager; 29 | import android.support.v7.widget.RecyclerView; 30 | import android.text.InputType; 31 | import android.util.Log; 32 | import android.view.Menu; 33 | import android.view.MenuItem; 34 | import android.view.View; 35 | import android.widget.EditText; 36 | import android.widget.ProgressBar; 37 | import android.widget.Toast; 38 | 39 | import com.codingwithmitch.googledirectionstest.R; 40 | import com.codingwithmitch.googledirectionstest.UserClient; 41 | import com.codingwithmitch.googledirectionstest.adapters.ChatroomRecyclerAdapter; 42 | import com.codingwithmitch.googledirectionstest.models.Chatroom; 43 | import com.codingwithmitch.googledirectionstest.models.UserLocation; 44 | import com.codingwithmitch.googledirectionstest.models.User; 45 | import com.codingwithmitch.googledirectionstest.services.LocationService; 46 | import com.google.android.gms.common.ConnectionResult; 47 | import com.google.android.gms.common.GoogleApiAvailability; 48 | import com.google.android.gms.location.FusedLocationProviderClient; 49 | import com.google.android.gms.location.LocationCallback; 50 | import com.google.android.gms.location.LocationRequest; 51 | import com.google.android.gms.location.LocationResult; 52 | import com.google.android.gms.location.LocationServices; 53 | import com.google.android.gms.location.LocationSettingsRequest; 54 | import com.google.android.gms.location.SettingsClient; 55 | import com.google.android.gms.tasks.OnCompleteListener; 56 | import com.google.android.gms.tasks.Task; 57 | import com.google.firebase.auth.FirebaseAuth; 58 | import com.google.firebase.firestore.CollectionReference; 59 | import com.google.firebase.firestore.DocumentReference; 60 | import com.google.firebase.firestore.DocumentSnapshot; 61 | import com.google.firebase.firestore.EventListener; 62 | import com.google.firebase.firestore.FirebaseFirestore; 63 | import com.google.firebase.firestore.FirebaseFirestoreException; 64 | import com.google.firebase.firestore.GeoPoint; 65 | import com.google.firebase.firestore.ListenerRegistration; 66 | import com.google.firebase.firestore.QueryDocumentSnapshot; 67 | import com.google.firebase.firestore.QuerySnapshot; 68 | 69 | import java.util.ArrayList; 70 | import java.util.Date; 71 | import java.util.HashMap; 72 | import java.util.HashSet; 73 | import java.util.Set; 74 | 75 | import javax.annotation.Nullable; 76 | 77 | import static com.codingwithmitch.googledirectionstest.Constants.ERROR_DIALOG_REQUEST; 78 | import static com.codingwithmitch.googledirectionstest.Constants.PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION; 79 | 80 | public class MainActivity extends AppCompatActivity implements 81 | View.OnClickListener, 82 | ChatroomRecyclerAdapter.ChatroomRecyclerClickListener { 83 | 84 | private static final String TAG = "MainActivity"; 85 | private final static long UPDATE_INTERVAL = 4 * 1000; /* 4 secs */ 86 | private final static long FASTEST_INTERVAL = 2000; /* 2 sec */ 87 | 88 | //widgets 89 | private ProgressBar mProgressBar; 90 | 91 | //vars 92 | private ArrayList mChatrooms = new ArrayList<>(); 93 | private Set mChatroomIds = new HashSet<>(); 94 | private ChatroomRecyclerAdapter mChatroomRecyclerAdapter; 95 | private RecyclerView mChatroomRecyclerView; 96 | private ListenerRegistration mChatroomEventListener; 97 | private FirebaseFirestore mDb; 98 | private boolean mLocationPermissionGranted = false; 99 | private FusedLocationProviderClient mFusedLocationClient; 100 | private LocationRequest mLocationRequest; 101 | 102 | 103 | @Override 104 | protected void onCreate(Bundle savedInstanceState) { 105 | super.onCreate(savedInstanceState); 106 | setContentView(R.layout.activity_main); 107 | mProgressBar = findViewById(R.id.progressBar); 108 | mChatroomRecyclerView = findViewById(R.id.chatrooms_recycler_view); 109 | 110 | findViewById(R.id.fab_create_chatroom).setOnClickListener(this); 111 | 112 | mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this); 113 | mDb = FirebaseFirestore.getInstance(); 114 | 115 | initSupportActionBar(); 116 | initChatroomRecyclerView(); 117 | 118 | if(((UserClient)getApplicationContext()).getUser() == null){ 119 | fetchAndSetUserClient(); 120 | } 121 | else{ 122 | getLocationPermission(); 123 | } 124 | } 125 | 126 | private void fetchAndSetUserClient(){ 127 | DocumentReference userRef = mDb.collection(getString(R.string.collection_users)) 128 | .document(FirebaseAuth.getInstance().getUid()); 129 | 130 | userRef.get().addOnCompleteListener(new OnCompleteListener() { 131 | @Override 132 | public void onComplete(@NonNull Task task) { 133 | if(task.isSuccessful()){ 134 | Log.d(TAG, "setupFirebaseAuth: successfully set the user client."); 135 | User user = task.getResult().toObject(User.class); 136 | ((UserClient)(getApplicationContext())).setUser(user); 137 | getLocationPermission(); 138 | } 139 | } 140 | }); 141 | } 142 | 143 | private void initSupportActionBar(){ 144 | setTitle("Chatrooms"); 145 | } 146 | 147 | 148 | public boolean isMapsEnabled(){ 149 | final LocationManager manager = (LocationManager) getSystemService( Context.LOCATION_SERVICE ); 150 | 151 | if ( !manager.isProviderEnabled( LocationManager.GPS_PROVIDER ) ) { 152 | buildAlertMessageNoGps(); 153 | return false; 154 | } 155 | return true; 156 | } 157 | 158 | private void buildAlertMessageNoGps() { 159 | final AlertDialog.Builder builder = new AlertDialog.Builder(this); 160 | builder.setMessage("Your GPS seems to be disabled, do you want to enable it?") 161 | .setCancelable(false) 162 | .setPositiveButton("Yes", new DialogInterface.OnClickListener() { 163 | public void onClick(@SuppressWarnings("unused") final DialogInterface dialog, @SuppressWarnings("unused") final int id) { 164 | startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS)); 165 | } 166 | }) 167 | .setNegativeButton("No", new DialogInterface.OnClickListener() { 168 | public void onClick(final DialogInterface dialog, @SuppressWarnings("unused") final int id) { 169 | dialog.cancel(); 170 | } 171 | }); 172 | final AlertDialog alert = builder.create(); 173 | alert.show(); 174 | } 175 | 176 | public boolean isServicesOK(){ 177 | Log.d(TAG, "isServicesOK: checking google services version"); 178 | 179 | int available = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(MainActivity.this); 180 | 181 | if(available == ConnectionResult.SUCCESS){ 182 | //everything is fine and the user can make map requests 183 | Log.d(TAG, "isServicesOK: Google Play Services is working"); 184 | return true; 185 | } 186 | else if(GoogleApiAvailability.getInstance().isUserResolvableError(available)){ 187 | //an error occured but we can resolve it 188 | Log.d(TAG, "isServicesOK: an error occured but we can fix it"); 189 | Dialog dialog = GoogleApiAvailability.getInstance().getErrorDialog(MainActivity.this, available, ERROR_DIALOG_REQUEST); 190 | dialog.show(); 191 | }else{ 192 | Toast.makeText(this, "You can't make map requests", Toast.LENGTH_SHORT).show(); 193 | } 194 | return false; 195 | } 196 | 197 | private void getLocationPermission() { 198 | /* 199 | * Request location permission, so that we can get the location of the 200 | * device. The result of the permission request is handled by a callback, 201 | * onRequestPermissionsResult. 202 | */ 203 | if (ContextCompat.checkSelfPermission(this.getApplicationContext(), 204 | android.Manifest.permission.ACCESS_FINE_LOCATION) 205 | == PackageManager.PERMISSION_GRANTED) { 206 | getLastKnownLocation(); 207 | // startLocationService(); 208 | } else { 209 | ActivityCompat.requestPermissions(this, 210 | new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION}, 211 | PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION); 212 | } 213 | } 214 | 215 | @Override 216 | public void onRequestPermissionsResult(int requestCode, 217 | @NonNull String permissions[], 218 | @NonNull int[] grantResults) { 219 | mLocationPermissionGranted = false; 220 | switch (requestCode) { 221 | case PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION: { 222 | // If request is cancelled, the result arrays are empty. 223 | if (grantResults.length > 0 224 | && grantResults[0] == PackageManager.PERMISSION_GRANTED) { 225 | getLastKnownLocation(); 226 | // startLocationService(); 227 | } 228 | } 229 | } 230 | } 231 | 232 | 233 | private void getLastKnownLocation() { 234 | Log.d(TAG, "getLastKnownLocation: called."); 235 | if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED 236 | && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { 237 | return; 238 | } 239 | mFusedLocationClient.getLastLocation().addOnCompleteListener(new OnCompleteListener() { 240 | @Override 241 | public void onComplete(@NonNull Task task) { 242 | if (task.isSuccessful()) { 243 | mLocationPermissionGranted = true; 244 | Location location = task.getResult(); 245 | User user = ((UserClient)(getApplicationContext())).getUser(); 246 | GeoPoint geoPoint = new GeoPoint(location.getLatitude(), location.getLongitude()); 247 | UserLocation userLocation = new UserLocation(user, geoPoint, null); 248 | saveUserLocation(userLocation); 249 | startLocationService(); 250 | } 251 | } 252 | }); 253 | 254 | 255 | 256 | // ---------------------------------- LocationRequest for Activity ------------------------------------ 257 | // Create the location request to start receiving updates 258 | // mLocationRequest = new LocationRequest(); 259 | //// mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY); 260 | // mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); 261 | //// mLocationRequest.setPriority(LocationRequest.PRIORITY_LOW_POWER); 262 | //// mLocationRequest.setPriority(LocationRequest.PRIORITY_NO_POWER); 263 | // mLocationRequest.setInterval(UPDATE_INTERVAL); 264 | // mLocationRequest.setFastestInterval(FASTEST_INTERVAL); 265 | // 266 | // // Create LocationSettingsRequest object using location request 267 | // LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder(); 268 | // builder.addLocationRequest(mLocationRequest); 269 | // LocationSettingsRequest locationSettingsRequest = builder.build(); 270 | // 271 | // // Check whether location settings are satisfied 272 | // // https://developers.google.com/android/reference/com/google/android/gms/location/SettingsClient 273 | // SettingsClient settingsClient = LocationServices.getSettingsClient(this); 274 | // settingsClient.checkLocationSettings(locationSettingsRequest); 275 | // 276 | // // new Google API SDK v11 uses getFusedLocationProviderClient(this) 277 | // mFusedLocationClient.requestLocationUpdates(mLocationRequest, new LocationCallback() { 278 | // @Override 279 | // public void onLocationResult(LocationResult locationResult) { 280 | // 281 | // mLocationPermissionGranted = true; 282 | // Location location = locationResult.getLastLocation(); 283 | // 284 | // if(location != null){ 285 | // User user = ((UserClient)(getApplicationContext())).getUser(); 286 | // GeoPoint geoPoint = new GeoPoint(location.getLatitude(), location.getLongitude()); 287 | // UserLocation userLocation = new UserLocation(user, geoPoint, null); 288 | // saveUserLocation(userLocation); 289 | // 290 | // Toast.makeText(getApplicationContext(), "current location:\n" 291 | // + "latitude: " + locationResult.getLastLocation().getLatitude() + "\n" 292 | // + "longitude: " + locationResult.getLastLocation().getLongitude(), 293 | // Toast.LENGTH_SHORT).show(); 294 | // } 295 | // 296 | // } 297 | // }, 298 | // Looper.myLooper()); 299 | 300 | 301 | } 302 | 303 | private void startLocationService(){ 304 | if(!isLocationServiceRunning()){ 305 | Intent serviceIntent = new Intent(this, LocationService.class); 306 | // this.startService(serviceIntent); 307 | 308 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O){ 309 | 310 | MainActivity.this.startForegroundService(serviceIntent); 311 | }else{ 312 | startService(serviceIntent); 313 | } 314 | } 315 | } 316 | 317 | private boolean isLocationServiceRunning() { 318 | ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE); 319 | for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)){ 320 | if("com.codingwithmitch.googledirectionstest.services.LocationService".equals(service.service.getClassName())) { 321 | Log.d(TAG, "isLocationServiceRunning: location service is already running."); 322 | return true; 323 | } 324 | } 325 | Log.d(TAG, "isLocationServiceRunning: location service is not running."); 326 | return false; 327 | } 328 | 329 | private void saveUserLocation(final UserLocation userLocation){ 330 | 331 | DocumentReference locationRef = mDb 332 | .collection(getString(R.string.collection_user_locations)) 333 | .document(FirebaseAuth.getInstance().getUid()); 334 | 335 | locationRef.set(userLocation).addOnCompleteListener(new OnCompleteListener() { 336 | @Override 337 | public void onComplete(@NonNull Task task) { 338 | if(task.isSuccessful()){ 339 | Log.d(TAG, "onComplete: \ninserted user location into database." + 340 | "\n latitude: " + userLocation.getGeo_point().getLatitude() + 341 | "\n longitude: " + userLocation.getGeo_point().getLongitude()); 342 | } 343 | } 344 | }); 345 | } 346 | 347 | 348 | @Override 349 | public void onClick(View view) { 350 | switch (view.getId()){ 351 | 352 | case R.id.fab_create_chatroom:{ 353 | if(checkMapServices()){ 354 | if(mLocationPermissionGranted){ 355 | newChatroomDialog(); 356 | } 357 | else{ 358 | getLocationPermission(); 359 | } 360 | } 361 | } 362 | } 363 | } 364 | 365 | private boolean checkMapServices(){ 366 | if(isServicesOK()){ 367 | if(isMapsEnabled()){ 368 | return true; 369 | } 370 | } 371 | return false; 372 | } 373 | 374 | private void initChatroomRecyclerView(){ 375 | mChatroomRecyclerAdapter = new ChatroomRecyclerAdapter(mChatrooms, this); 376 | mChatroomRecyclerView.setAdapter(mChatroomRecyclerAdapter); 377 | mChatroomRecyclerView.setLayoutManager(new LinearLayoutManager(this)); 378 | } 379 | 380 | private void getChatrooms(){ 381 | 382 | CollectionReference chatroomsCollection = mDb 383 | .collection(getString(R.string.collection_chatrooms)); 384 | 385 | mChatroomEventListener = chatroomsCollection.addSnapshotListener(new EventListener() { 386 | @Override 387 | public void onEvent(@Nullable QuerySnapshot queryDocumentSnapshots, @Nullable FirebaseFirestoreException e) { 388 | Log.d(TAG, "onEvent: called."); 389 | 390 | if (e != null) { 391 | Log.e(TAG, "onEvent: Listen failed.", e); 392 | return; 393 | } 394 | 395 | if(queryDocumentSnapshots != null){ 396 | for (QueryDocumentSnapshot doc : queryDocumentSnapshots) { 397 | 398 | Chatroom chatroom = doc.toObject(Chatroom.class); 399 | if(!mChatroomIds.contains(chatroom.getChatroom_id())){ 400 | mChatroomIds.add(chatroom.getChatroom_id()); 401 | mChatrooms.add(chatroom); 402 | } 403 | } 404 | Log.d(TAG, "onEvent: number of chatrooms: " + mChatrooms.size()); 405 | mChatroomRecyclerAdapter.notifyDataSetChanged(); 406 | } 407 | 408 | } 409 | }); 410 | } 411 | 412 | private void buildNewChatroom(String chatroomName){ 413 | 414 | final Chatroom chatroom = new Chatroom(); 415 | chatroom.setTitle(chatroomName); 416 | 417 | DocumentReference newChatroomRef = mDb 418 | .collection(getString(R.string.collection_chatrooms)) 419 | .document(); 420 | 421 | chatroom.setChatroom_id(newChatroomRef.getId()); 422 | 423 | newChatroomRef.set(chatroom).addOnCompleteListener(new OnCompleteListener() { 424 | @Override 425 | public void onComplete(@NonNull Task task) { 426 | hideDialog(); 427 | 428 | if(task.isSuccessful()){ 429 | navChatroomActivity(chatroom); 430 | }else{ 431 | View parentLayout = findViewById(android.R.id.content); 432 | Snackbar.make(parentLayout, "Something went wrong.", Snackbar.LENGTH_SHORT).show(); 433 | } 434 | } 435 | }); 436 | } 437 | 438 | private void navChatroomActivity(Chatroom chatroom){ 439 | Intent intent = new Intent(MainActivity.this, ChatroomActivity.class); 440 | intent.putExtra(getString(R.string.intent_chatroom), chatroom); 441 | startActivity(intent); 442 | } 443 | 444 | private void newChatroomDialog(){ 445 | 446 | AlertDialog.Builder builder = new AlertDialog.Builder(this); 447 | builder.setTitle("Enter a chatroom name"); 448 | 449 | final EditText input = new EditText(this); 450 | input.setInputType(InputType.TYPE_CLASS_TEXT); 451 | builder.setView(input); 452 | 453 | builder.setPositiveButton("CREATE", new DialogInterface.OnClickListener() { 454 | @Override 455 | public void onClick(DialogInterface dialog, int which) { 456 | if(!input.getText().toString().equals("")){ 457 | buildNewChatroom(input.getText().toString()); 458 | } 459 | else { 460 | Toast.makeText(MainActivity.this, "Enter a chatroom name", Toast.LENGTH_SHORT).show(); 461 | } 462 | } 463 | }); 464 | builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { 465 | @Override 466 | public void onClick(DialogInterface dialog, int which) { 467 | dialog.cancel(); 468 | } 469 | }); 470 | 471 | builder.show(); 472 | } 473 | 474 | @Override 475 | protected void onDestroy() { 476 | super.onDestroy(); 477 | if(mChatroomEventListener != null){ 478 | mChatroomEventListener.remove(); 479 | } 480 | } 481 | 482 | @Override 483 | protected void onResume() { 484 | super.onResume(); 485 | getChatrooms(); 486 | } 487 | 488 | @Override 489 | public void onChatroomSelected(int position) { 490 | if(checkMapServices()){ 491 | if(mLocationPermissionGranted){ 492 | navChatroomActivity(mChatrooms.get(position)); 493 | } 494 | else{ 495 | getLocationPermission(); 496 | } 497 | } 498 | } 499 | 500 | private void signOut(){ 501 | FirebaseAuth.getInstance().signOut(); // Sign out Firebase 502 | 503 | ((UserClient)getApplicationContext()).setUser(null); // Reset UserClient instance 504 | 505 | Intent intent = new Intent(this, LoginActivity.class); 506 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 507 | startActivity(intent); 508 | finish(); 509 | } 510 | 511 | @Override 512 | public boolean onCreateOptionsMenu(Menu menu) { 513 | getMenuInflater().inflate(R.menu.menu_main, menu); 514 | return super.onCreateOptionsMenu(menu); 515 | } 516 | 517 | 518 | @Override 519 | public boolean onOptionsItemSelected(MenuItem item) { 520 | switch(item.getItemId()){ 521 | case R.id.action_sign_out:{ 522 | signOut(); 523 | return true; 524 | } 525 | case R.id.action_profile:{ 526 | startActivity(new Intent(this, ProfileActivity.class)); 527 | return true; 528 | } 529 | default:{ 530 | return super.onOptionsItemSelected(item); 531 | } 532 | } 533 | 534 | } 535 | 536 | private void showDialog(){ 537 | mProgressBar.setVisibility(View.VISIBLE); 538 | } 539 | 540 | private void hideDialog(){ 541 | mProgressBar.setVisibility(View.GONE); 542 | } 543 | 544 | 545 | } 546 | -------------------------------------------------------------------------------- /app/src/main/java/com/codingwithmitch/googledirectionstest/ui/ProfileActivity.java: -------------------------------------------------------------------------------- 1 | package com.codingwithmitch.googledirectionstest.ui; 2 | 3 | import android.animation.ObjectAnimator; 4 | import android.os.Bundle; 5 | import android.support.annotation.NonNull; 6 | import android.support.design.widget.FloatingActionButton; 7 | import android.support.design.widget.Snackbar; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.support.v7.widget.Toolbar; 10 | import android.util.Log; 11 | import android.view.Menu; 12 | import android.view.MenuItem; 13 | import android.view.View; 14 | 15 | import com.bumptech.glide.Glide; 16 | import com.bumptech.glide.request.RequestOptions; 17 | import com.codingwithmitch.googledirectionstest.R; 18 | import com.codingwithmitch.googledirectionstest.UserClient; 19 | import com.codingwithmitch.googledirectionstest.adapters.ImageListRecyclerAdapter; 20 | import com.codingwithmitch.googledirectionstest.models.User; 21 | import com.codingwithmitch.googledirectionstest.util.ViewWeightAnimationWrapper; 22 | import com.google.android.gms.tasks.OnCompleteListener; 23 | import com.google.android.gms.tasks.Task; 24 | import com.google.firebase.auth.FirebaseAuth; 25 | import com.google.firebase.firestore.DocumentSnapshot; 26 | import com.google.firebase.firestore.FirebaseFirestore; 27 | 28 | import de.hdodenhof.circleimageview.CircleImageView; 29 | 30 | public class ProfileActivity extends AppCompatActivity implements 31 | View.OnClickListener, 32 | IProfile 33 | { 34 | 35 | private static final String TAG = "ProfileActivity"; 36 | 37 | 38 | //widgets 39 | private CircleImageView mAvatarImage; 40 | 41 | //vars 42 | private ImageListFragment mImageListFragment; 43 | 44 | 45 | @Override 46 | protected void onCreate(Bundle savedInstanceState) { 47 | super.onCreate(savedInstanceState); 48 | setContentView(R.layout.activity_profile); 49 | getSupportActionBar().setDisplayHomeAsUpEnabled(true); 50 | getSupportActionBar().setHomeButtonEnabled(true); 51 | mAvatarImage = findViewById(R.id.image_choose_avatar); 52 | 53 | findViewById(R.id.image_choose_avatar).setOnClickListener(this); 54 | findViewById(R.id.text_choose_avatar).setOnClickListener(this); 55 | 56 | retrieveProfileImage(); 57 | } 58 | 59 | private void retrieveProfileImage(){ 60 | RequestOptions requestOptions = new RequestOptions() 61 | .error(R.drawable.cwm_logo) 62 | .placeholder(R.drawable.cwm_logo); 63 | 64 | int avatar = 0; 65 | try{ 66 | avatar = Integer.parseInt(((UserClient)getApplicationContext()).getUser().getAvatar()); 67 | }catch (NumberFormatException e){ 68 | Log.e(TAG, "retrieveProfileImage: no avatar image. Setting default. " + e.getMessage() ); 69 | } 70 | 71 | Glide.with(ProfileActivity.this) 72 | .setDefaultRequestOptions(requestOptions) 73 | .load(avatar) 74 | .into(mAvatarImage); 75 | } 76 | 77 | @Override 78 | public void onClick(View v) { 79 | mImageListFragment = new ImageListFragment(); 80 | getSupportFragmentManager().beginTransaction() 81 | .setCustomAnimations(R.anim.slide_in_up, R.anim.slide_in_down, R.anim.slide_out_down, R.anim.slide_out_up) 82 | .replace(R.id.fragment_container, mImageListFragment, getString(R.string.fragment_image_list)) 83 | .commit(); 84 | } 85 | 86 | @Override 87 | public boolean onOptionsItemSelected(MenuItem item) { 88 | 89 | switch (item.getItemId()){ 90 | case android.R.id.home:{ 91 | finish(); 92 | } 93 | } 94 | return super.onOptionsItemSelected(item); 95 | } 96 | 97 | @Override 98 | public boolean onCreateOptionsMenu(Menu menu) { 99 | return super.onCreateOptionsMenu(menu); 100 | } 101 | 102 | @Override 103 | public void onImageSelected(int resource) { 104 | 105 | // remove the image selector fragment 106 | getSupportFragmentManager().beginTransaction() 107 | .setCustomAnimations(R.anim.slide_in_up, R.anim.slide_in_down, R.anim.slide_out_down, R.anim.slide_out_up) 108 | .remove(mImageListFragment) 109 | .commit(); 110 | 111 | // display the image 112 | RequestOptions requestOptions = new RequestOptions() 113 | .placeholder(R.drawable.cwm_logo) 114 | .error(R.drawable.cwm_logo); 115 | 116 | Glide.with(this) 117 | .setDefaultRequestOptions(requestOptions) 118 | .load(resource) 119 | .into(mAvatarImage); 120 | 121 | // update the client and database 122 | User user = ((UserClient)getApplicationContext()).getUser(); 123 | user.setAvatar(String.valueOf(resource)); 124 | 125 | FirebaseFirestore.getInstance() 126 | .collection(getString(R.string.collection_users)) 127 | .document(FirebaseAuth.getInstance().getUid()) 128 | .set(user); 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /app/src/main/java/com/codingwithmitch/googledirectionstest/ui/RegisterActivity.java: -------------------------------------------------------------------------------- 1 | package com.codingwithmitch.googledirectionstest.ui; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.annotation.NonNull; 6 | import android.support.annotation.Nullable; 7 | import android.support.design.widget.Snackbar; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.util.Log; 10 | import android.view.View; 11 | import android.view.WindowManager; 12 | import android.widget.Button; 13 | import android.widget.EditText; 14 | import android.widget.ProgressBar; 15 | import android.widget.Toast; 16 | 17 | import com.codingwithmitch.googledirectionstest.R; 18 | import com.codingwithmitch.googledirectionstest.models.User; 19 | import com.google.android.gms.tasks.OnCompleteListener; 20 | import com.google.android.gms.tasks.OnFailureListener; 21 | import com.google.android.gms.tasks.Task; 22 | import com.google.firebase.auth.AuthResult; 23 | import com.google.firebase.auth.FirebaseAuth; 24 | import com.google.firebase.firestore.DocumentReference; 25 | import com.google.firebase.firestore.FirebaseFirestore; 26 | 27 | import static android.text.TextUtils.isEmpty; 28 | import static com.codingwithmitch.googledirectionstest.util.Check.doStringsMatch; 29 | 30 | 31 | public class RegisterActivity extends AppCompatActivity implements 32 | View.OnClickListener 33 | { 34 | private static final String TAG = "RegisterActivity"; 35 | 36 | //widgets 37 | private EditText mEmail, mPassword, mConfirmPassword; 38 | private ProgressBar mProgressBar; 39 | 40 | //vars 41 | private FirebaseFirestore mDb; 42 | 43 | 44 | @Override 45 | protected void onCreate(@Nullable Bundle savedInstanceState) { 46 | super.onCreate(savedInstanceState); 47 | setContentView(R.layout.activity_register); 48 | mEmail = (EditText) findViewById(R.id.input_email); 49 | mPassword = (EditText) findViewById(R.id.input_password); 50 | mConfirmPassword = (EditText) findViewById(R.id.input_confirm_password); 51 | mProgressBar = (ProgressBar) findViewById(R.id.progressBar); 52 | 53 | findViewById(R.id.btn_register).setOnClickListener(this); 54 | 55 | mDb = FirebaseFirestore.getInstance(); 56 | 57 | hideSoftKeyboard(); 58 | } 59 | 60 | /** 61 | * Register a new email and password to Firebase Authentication 62 | * @param email 63 | * @param password 64 | */ 65 | public void registerNewEmail(final String email, String password){ 66 | 67 | showDialog(); 68 | 69 | FirebaseAuth.getInstance().createUserWithEmailAndPassword(email, password) 70 | .addOnCompleteListener(new OnCompleteListener() { 71 | @Override 72 | public void onComplete(@NonNull Task task) { 73 | Log.d(TAG, "createUserWithEmail:onComplete:" + task.isSuccessful()); 74 | 75 | if (task.isSuccessful()){ 76 | Log.d(TAG, "onComplete: AuthState: " + FirebaseAuth.getInstance().getCurrentUser().getUid()); 77 | 78 | //insert some default data 79 | User user = new User(); 80 | user.setEmail(email); 81 | user.setUsername(email.substring(0, email.indexOf("@"))); 82 | user.setUser_id(FirebaseAuth.getInstance().getUid()); 83 | 84 | DocumentReference newUserRef = mDb 85 | .collection(getString(R.string.collection_users)) 86 | .document(FirebaseAuth.getInstance().getUid()); 87 | 88 | newUserRef.set(user).addOnCompleteListener(new OnCompleteListener() { 89 | @Override 90 | public void onComplete(@NonNull Task task) { 91 | hideDialog(); 92 | 93 | if(task.isSuccessful()){ 94 | redirectLoginScreen(); 95 | }else{ 96 | View parentLayout = findViewById(android.R.id.content); 97 | Snackbar.make(parentLayout, "Something went wrong.", Snackbar.LENGTH_SHORT).show(); 98 | } 99 | } 100 | }); 101 | 102 | } 103 | else { 104 | View parentLayout = findViewById(android.R.id.content); 105 | Snackbar.make(parentLayout, "Something went wrong.", Snackbar.LENGTH_SHORT).show(); 106 | hideDialog(); 107 | } 108 | 109 | // ... 110 | } 111 | }); 112 | } 113 | 114 | /** 115 | * Redirects the user to the login screen 116 | */ 117 | private void redirectLoginScreen(){ 118 | Log.d(TAG, "redirectLoginScreen: redirecting to login screen."); 119 | 120 | Intent intent = new Intent(RegisterActivity.this, LoginActivity.class); 121 | startActivity(intent); 122 | finish(); 123 | } 124 | 125 | 126 | private void showDialog(){ 127 | mProgressBar.setVisibility(View.VISIBLE); 128 | 129 | } 130 | 131 | private void hideDialog(){ 132 | if(mProgressBar.getVisibility() == View.VISIBLE){ 133 | mProgressBar.setVisibility(View.INVISIBLE); 134 | } 135 | } 136 | 137 | private void hideSoftKeyboard(){ 138 | this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); 139 | } 140 | 141 | @Override 142 | public void onClick(View view) { 143 | switch (view.getId()){ 144 | case R.id.btn_register:{ 145 | Log.d(TAG, "onClick: attempting to register."); 146 | 147 | //check for null valued EditText fields 148 | if(!isEmpty(mEmail.getText().toString()) 149 | && !isEmpty(mPassword.getText().toString()) 150 | && !isEmpty(mConfirmPassword.getText().toString())){ 151 | 152 | //check if passwords match 153 | if(doStringsMatch(mPassword.getText().toString(), mConfirmPassword.getText().toString())){ 154 | 155 | //Initiate registration task 156 | registerNewEmail(mEmail.getText().toString(), mPassword.getText().toString()); 157 | }else{ 158 | Toast.makeText(RegisterActivity.this, "Passwords do not Match", Toast.LENGTH_SHORT).show(); 159 | } 160 | 161 | }else{ 162 | Toast.makeText(RegisterActivity.this, "You must fill out all the fields", Toast.LENGTH_SHORT).show(); 163 | } 164 | break; 165 | } 166 | } 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /app/src/main/java/com/codingwithmitch/googledirectionstest/ui/UserListFragment.java: -------------------------------------------------------------------------------- 1 | package com.codingwithmitch.googledirectionstest.ui; 2 | 3 | import android.animation.ObjectAnimator; 4 | import android.app.AlertDialog; 5 | import android.content.Context; 6 | import android.content.DialogInterface; 7 | import android.content.Intent; 8 | import android.graphics.Bitmap; 9 | import android.graphics.BitmapFactory; 10 | import android.graphics.Canvas; 11 | import android.graphics.Color; 12 | import android.graphics.Paint; 13 | import android.graphics.PorterDuff; 14 | import android.graphics.drawable.BitmapDrawable; 15 | import android.graphics.drawable.Drawable; 16 | import android.net.Uri; 17 | import android.os.Bundle; 18 | import android.os.Handler; 19 | import android.os.Looper; 20 | import android.support.annotation.DrawableRes; 21 | import android.support.annotation.NonNull; 22 | import android.support.annotation.Nullable; 23 | import android.support.design.widget.Snackbar; 24 | import android.support.v4.app.Fragment; 25 | import android.support.v4.content.ContextCompat; 26 | import android.support.v7.widget.LinearLayoutManager; 27 | import android.support.v7.widget.RecyclerView; 28 | import android.util.Log; 29 | import android.view.LayoutInflater; 30 | import android.view.View; 31 | import android.view.ViewGroup; 32 | import android.view.inputmethod.InputMethodManager; 33 | import android.widget.ImageView; 34 | import android.widget.ProgressBar; 35 | import android.widget.RelativeLayout; 36 | import android.widget.Toast; 37 | 38 | import com.codingwithmitch.googledirectionstest.R; 39 | import com.codingwithmitch.googledirectionstest.adapters.UserRecyclerAdapter; 40 | import com.codingwithmitch.googledirectionstest.models.ClusterMarker; 41 | import com.codingwithmitch.googledirectionstest.models.PolylineData; 42 | import com.codingwithmitch.googledirectionstest.models.User; 43 | import com.codingwithmitch.googledirectionstest.models.UserLocation; 44 | import com.codingwithmitch.googledirectionstest.models.UserMarker; 45 | import com.codingwithmitch.googledirectionstest.util.MyClusterManagerRenderer; 46 | import com.codingwithmitch.googledirectionstest.util.ViewWeightAnimationWrapper; 47 | import com.google.android.gms.maps.CameraUpdateFactory; 48 | import com.google.android.gms.maps.GoogleMap; 49 | import com.google.android.gms.maps.MapView; 50 | import com.google.android.gms.maps.OnMapReadyCallback; 51 | 52 | import com.google.android.gms.maps.model.BitmapDescriptorFactory; 53 | import com.google.android.gms.maps.model.LatLng; 54 | import com.google.android.gms.maps.model.LatLngBounds; 55 | import com.google.android.gms.maps.model.Marker; 56 | import com.google.android.gms.maps.model.MarkerOptions; 57 | import com.google.android.gms.maps.model.Polyline; 58 | import com.google.android.gms.maps.model.PolylineOptions; 59 | import com.google.android.gms.tasks.OnCompleteListener; 60 | import com.google.android.gms.tasks.Task; 61 | import com.google.firebase.auth.FirebaseAuth; 62 | import com.google.firebase.firestore.CollectionReference; 63 | import com.google.firebase.firestore.DocumentReference; 64 | import com.google.firebase.firestore.DocumentSnapshot; 65 | import com.google.firebase.firestore.FirebaseFirestore; 66 | import com.google.gson.Gson; 67 | import com.google.gson.GsonBuilder; 68 | import com.google.maps.DirectionsApiRequest; 69 | import com.google.maps.DistanceMatrixApiRequest; 70 | import com.google.maps.GeoApiContext; 71 | import com.google.maps.PendingResult; 72 | import com.google.maps.android.clustering.Cluster; 73 | import com.google.maps.android.clustering.ClusterManager; 74 | import com.google.maps.android.clustering.algo.Algorithm; 75 | import com.google.maps.android.clustering.algo.NonHierarchicalDistanceBasedAlgorithm; 76 | import com.google.maps.internal.PolylineEncoding; 77 | import com.google.maps.model.DirectionsResult; 78 | import com.google.maps.model.DirectionsRoute; 79 | import com.google.maps.model.DistanceMatrix; 80 | import com.google.maps.model.DistanceMatrixElement; 81 | import com.google.maps.model.DistanceMatrixRow; 82 | 83 | 84 | import java.util.ArrayList; 85 | import java.util.Collection; 86 | import java.util.List; 87 | import java.util.Set; 88 | 89 | 90 | import static com.codingwithmitch.googledirectionstest.Constants.MAPVIEW_BUNDLE_KEY; 91 | 92 | public class UserListFragment extends Fragment implements 93 | OnMapReadyCallback, 94 | UserRecyclerAdapter.UserListRecyclerClickListener, 95 | View.OnClickListener, 96 | GoogleMap.OnInfoWindowClickListener, 97 | GoogleMap.OnPolylineClickListener 98 | { 99 | 100 | private static final String TAG = "UserListFragment"; 101 | 102 | private static final int MAP_LAYOUT_STATE_CONTRACTED = 0; 103 | private static final int MAP_LAYOUT_STATE_EXPANDED = 1; 104 | private static final int LOCATION_UPDATE_INTERVAL = 3000; 105 | 106 | //widgets 107 | private RecyclerView mUserListRecyclerView; 108 | private MapView mMapView; 109 | private RelativeLayout mMapContainer; 110 | private ProgressBar mProgressBar; 111 | 112 | 113 | //vars 114 | private ArrayList mUserList = new ArrayList<>(); 115 | private ArrayList mUserLocations = new ArrayList<>(); 116 | private UserRecyclerAdapter mUserRecyclerAdapter; 117 | private GoogleMap mGoogleMap; 118 | private LatLngBounds mMapBoundary; 119 | private int mMapLayoutState = 0; 120 | private com.google.maps.model.LatLng mUserPosition ; 121 | private GeoApiContext mGeoApiContext; 122 | private ArrayList mPolyLinesData = new ArrayList<>(); 123 | private ArrayList mClusterMarkers = new ArrayList<>(); 124 | private Marker mSelectedMarker = null; 125 | private ArrayList mTripMarkers = new ArrayList<>(); 126 | private Handler mHandler = new Handler(); 127 | private Runnable mRunnable; 128 | private ClusterManager mClusterManager; 129 | private MyClusterManagerRenderer mClusterManagerRenderer; 130 | 131 | 132 | public static UserListFragment newInstance(){ 133 | return new UserListFragment(); 134 | } 135 | 136 | @Override 137 | public void onCreate(@Nullable Bundle savedInstanceState) { 138 | super.onCreate(savedInstanceState); 139 | Log.d(TAG, "LifeCycle Event: onCreate: called. "); 140 | if(mUserLocations.size() == 0){ // make sure the list doesn't duplicate by navigating back 141 | if(getArguments() != null){ 142 | final ArrayList users = getArguments().getParcelableArrayList(getString(R.string.intent_user_list)); 143 | mUserList.addAll(users); 144 | 145 | final ArrayList locations = getArguments().getParcelableArrayList(getString(R.string.intent_user_locations)); 146 | mUserLocations.addAll(locations); 147 | } 148 | } 149 | 150 | } 151 | 152 | @Nullable 153 | @Override 154 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 155 | Log.d(TAG, "LifeCycle Event: onCreateView: called. "); 156 | View view = inflater.inflate(R.layout.fragment_user_list, container, false); 157 | mUserListRecyclerView = view.findViewById(R.id.user_list_recycler_view); 158 | mMapContainer = view.findViewById(R.id.map_container); 159 | mProgressBar = view.findViewById(R.id.progressBar); 160 | mMapView = view.findViewById(R.id.user_list_map); 161 | view.findViewById(R.id.btn_full_screen_map).setOnClickListener(this); 162 | view.findViewById(R.id.btn_reset_map).setOnClickListener(this); 163 | 164 | initGoogleMap(savedInstanceState); 165 | initUserListRecyclerView(); 166 | hideSoftKeyboard(); 167 | 168 | return view; 169 | } 170 | 171 | private void initGoogleMap(Bundle savedInstanceState){ 172 | 173 | // *** IMPORTANT *** 174 | // MapView requires that the Bundle you pass contain _ONLY_ MapView SDK 175 | // objects or sub-Bundles. 176 | Bundle mapViewBundle = null; 177 | if (savedInstanceState != null) { 178 | mapViewBundle = savedInstanceState.getBundle(MAPVIEW_BUNDLE_KEY); 179 | } 180 | mMapView.onCreate(mapViewBundle); 181 | mMapView.getMapAsync(this); 182 | 183 | if(mGeoApiContext == null){ 184 | mGeoApiContext = new GeoApiContext.Builder() 185 | .apiKey(getString(R.string.google_maps_api_key)) 186 | .build(); 187 | } 188 | } 189 | 190 | @Override 191 | public void onSaveInstanceState(Bundle outState) { 192 | super.onSaveInstanceState(outState); 193 | 194 | Bundle mapViewBundle = outState.getBundle(MAPVIEW_BUNDLE_KEY); 195 | if (mapViewBundle == null) { 196 | mapViewBundle = new Bundle(); 197 | outState.putBundle(MAPVIEW_BUNDLE_KEY, mapViewBundle); 198 | } 199 | 200 | mMapView.onSaveInstanceState(mapViewBundle); 201 | } 202 | 203 | private void startUserLocationsRunnable(){ 204 | Log.d(TAG, "startUserLocationsRunnable: starting runnable for retrieving updated locations."); 205 | mHandler.postDelayed(mRunnable = new Runnable() { 206 | @Override 207 | public void run() { 208 | // If a trip has NOT been calculated, continue updating locations 209 | Log.d(TAG, "run: polylinesdata size: " + mPolyLinesData.size()); 210 | if(mPolyLinesData.size() == 0){ 211 | retrieveUserLocations(); 212 | mHandler.postDelayed(mRunnable, LOCATION_UPDATE_INTERVAL); 213 | } 214 | } 215 | }, LOCATION_UPDATE_INTERVAL); 216 | } 217 | 218 | private void retrieveUserLocations(){ 219 | Log.d(TAG, "retrieveUserLocations: retrieving location of all users in the chatroom."); 220 | 221 | try{ 222 | for(final ClusterMarker clusterMarker: mClusterMarkers){ 223 | 224 | DocumentReference userLocationRef = FirebaseFirestore.getInstance() 225 | .collection(getString(R.string.collection_user_locations)) 226 | .document(clusterMarker.getUser().getUser_id()); 227 | 228 | userLocationRef.get().addOnCompleteListener(new OnCompleteListener() { 229 | @Override 230 | public void onComplete(@NonNull Task task) { 231 | if(task.isSuccessful()){ 232 | 233 | final UserLocation updatedUserLocation = task.getResult().toObject(UserLocation.class); 234 | 235 | // update the location 236 | for (int i = 0; i < mClusterMarkers.size(); i++) { 237 | try { 238 | if (mClusterMarkers.get(i).getUser().getUser_id().equals(updatedUserLocation.getUser().getUser_id())) { 239 | 240 | LatLng updatedLatLng = new LatLng( 241 | updatedUserLocation.getGeo_point().getLatitude(), 242 | updatedUserLocation.getGeo_point().getLongitude() 243 | ); 244 | 245 | mClusterMarkers.get(i).setPosition(updatedLatLng); 246 | mClusterManagerRenderer.setUpdateMarker(mClusterMarkers.get(i)); 247 | } 248 | 249 | 250 | } catch (NullPointerException e) { 251 | Log.e(TAG, "retrieveUserLocations: NullPointerException: " + e.getMessage()); 252 | } 253 | } 254 | } 255 | } 256 | }); 257 | } 258 | }catch (IllegalStateException e){ 259 | Log.e(TAG, "retrieveUserLocations: Fragment was destroyed during Firestore query. Ending query." + e.getMessage() ); 260 | } 261 | 262 | } 263 | 264 | private void initUserListRecyclerView(){ 265 | mUserRecyclerAdapter = new UserRecyclerAdapter(mUserList, this); 266 | mUserListRecyclerView.setAdapter(mUserRecyclerAdapter); 267 | mUserListRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); 268 | } 269 | 270 | 271 | private void addMapMarkers(){ 272 | showProgressBar(); 273 | 274 | if(mGoogleMap != null){ 275 | 276 | resetMap(); 277 | 278 | if(mClusterManager == null){ 279 | mClusterManager = new ClusterManager(getActivity().getApplicationContext(), mGoogleMap); 280 | } 281 | if(mClusterManagerRenderer == null){ 282 | mClusterManagerRenderer = new MyClusterManagerRenderer( 283 | getActivity(), 284 | mGoogleMap, 285 | mClusterManager 286 | ); 287 | mClusterManagerRenderer.setMinClusterSize(5); 288 | mClusterManager.setRenderer(mClusterManagerRenderer); 289 | } 290 | mGoogleMap.setOnInfoWindowClickListener(this); 291 | 292 | for(UserLocation userLocation: mUserLocations){ 293 | 294 | Log.d(TAG, "addMapMarkers: location: " + userLocation.getGeo_point().toString()); 295 | try{ 296 | String snippet = ""; 297 | if(userLocation.getUser().getUser_id().equals(FirebaseAuth.getInstance().getUid())){ 298 | snippet = "This is you"; 299 | } 300 | else{ 301 | snippet = "Determine route to " + userLocation.getUser().getUsername() + "?"; 302 | } 303 | 304 | int avatar = R.drawable.cwm_logo; // set the default avatar 305 | try{ 306 | avatar = Integer.parseInt(userLocation.getUser().getAvatar()); 307 | }catch (NumberFormatException e){ 308 | Log.d(TAG, "addMapMarkers: no avatar for " + userLocation.getUser().getUsername() + ", setting default."); 309 | } 310 | ClusterMarker newClusterMarker = new ClusterMarker( 311 | new LatLng(userLocation.getGeo_point().getLatitude(), userLocation.getGeo_point().getLongitude()), 312 | userLocation.getUser().getUsername(), 313 | snippet, 314 | avatar, 315 | userLocation.getUser() 316 | ); 317 | mClusterManager.addItem(newClusterMarker); 318 | mClusterMarkers.add(newClusterMarker); 319 | 320 | // set the current users location to global variable 321 | if(FirebaseAuth.getInstance().getUid().equals(userLocation.getUser().getUser_id())){ 322 | mUserPosition = new com.google.maps.model.LatLng( 323 | userLocation.getGeo_point().getLatitude(), 324 | userLocation.getGeo_point().getLongitude() 325 | ); 326 | } 327 | }catch (NullPointerException e){ 328 | Log.e(TAG, "addMapMarkers: NullPointerException: " + e.getMessage() ); 329 | } 330 | 331 | } 332 | mClusterManager.cluster(); 333 | 334 | setCameraView(); 335 | hideProgressBar(); 336 | } 337 | } 338 | 339 | 340 | private void resetMap(){ 341 | if(mGoogleMap != null) { 342 | mGoogleMap.clear(); 343 | 344 | if(mClusterManager != null){ 345 | mClusterManager.clearItems(); 346 | } 347 | 348 | if (mClusterMarkers.size() > 0) { 349 | mClusterMarkers.clear(); 350 | mClusterMarkers = new ArrayList<>(); 351 | } 352 | 353 | if(mPolyLinesData.size() > 0){ 354 | mPolyLinesData.clear(); 355 | mPolyLinesData = new ArrayList<>(); 356 | } 357 | } 358 | } 359 | 360 | /** 361 | * Determines the view boundary then sets the camera 362 | */ 363 | private void setCameraView(){ 364 | 365 | // Set a boundary to start 366 | double bottomBoundary = mUserPosition.lat - .1; 367 | double leftBoundary = mUserPosition.lng - .1; 368 | double topBoundary = mUserPosition.lat + .1; 369 | double rightBoundary = mUserPosition.lng + .1; 370 | 371 | mMapBoundary = new LatLngBounds( 372 | new LatLng(bottomBoundary,leftBoundary), 373 | new LatLng(topBoundary, rightBoundary) 374 | ); 375 | 376 | mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngBounds(mMapBoundary, 0)); 377 | } 378 | 379 | @Override 380 | public void onUserSelected(int position) { 381 | Log.d(TAG, "onUserSelected: selected a user: " + mUserList.get(position).toString()); 382 | String selectedUserId = mUserList.get(position).getUser_id(); 383 | 384 | for(ClusterMarker clusterMarker: mClusterMarkers){ 385 | if(selectedUserId.equals(clusterMarker.getUser().getUser_id())){ 386 | mGoogleMap.animateCamera( 387 | CameraUpdateFactory.newLatLng( 388 | new LatLng(clusterMarker.getPosition().latitude, clusterMarker.getPosition().longitude)), 389 | 600, 390 | null 391 | ); 392 | break; 393 | } 394 | } 395 | } 396 | 397 | @Override 398 | public void onClick(View v) { 399 | switch (v.getId()){ 400 | case R.id.btn_full_screen_map:{ 401 | 402 | if(mMapLayoutState == MAP_LAYOUT_STATE_CONTRACTED){ 403 | mMapLayoutState = MAP_LAYOUT_STATE_EXPANDED; 404 | expandMapAnimation(); 405 | } 406 | else if(mMapLayoutState == MAP_LAYOUT_STATE_EXPANDED){ 407 | mMapLayoutState = MAP_LAYOUT_STATE_CONTRACTED; 408 | contractMapAnimation(); 409 | } 410 | break; 411 | } 412 | 413 | case R.id.btn_reset_map:{ 414 | addMapMarkers(); 415 | startUserLocationsRunnable(); 416 | break; 417 | } 418 | } 419 | } 420 | 421 | @Override 422 | public void onInfoWindowClick(final Marker marker) { 423 | Log.d(TAG, "onInfoWindowClick: marker id: " + marker.getId().replace("m", "")); 424 | 425 | if(marker.getTitle().contains("Trip #")){ 426 | final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); 427 | builder.setMessage("Open Google Maps?") 428 | .setCancelable(true) 429 | .setPositiveButton("Yes", new DialogInterface.OnClickListener() { 430 | public void onClick(@SuppressWarnings("unused") final DialogInterface dialog, @SuppressWarnings("unused") final int id) { 431 | String latitude = String.valueOf(marker.getPosition().latitude); 432 | String longitude = String.valueOf(marker.getPosition().longitude); 433 | Uri gmmIntentUri = Uri.parse("google.navigation:q=" + latitude + "," + longitude); 434 | Intent mapIntent = new Intent(Intent.ACTION_VIEW, gmmIntentUri); 435 | mapIntent.setPackage("com.google.android.apps.maps"); 436 | 437 | try{ 438 | if (mapIntent.resolveActivity(getActivity().getPackageManager()) != null) { 439 | startActivity(mapIntent); 440 | } 441 | }catch (NullPointerException e){ 442 | Log.e(TAG, "onClick: NullPointerException: Couldn't open map." + e.getMessage() ); 443 | Toast.makeText(getActivity(), "Couldn't open map", Toast.LENGTH_SHORT).show(); 444 | } 445 | 446 | } 447 | }) 448 | .setNegativeButton("No", new DialogInterface.OnClickListener() { 449 | public void onClick(final DialogInterface dialog, @SuppressWarnings("unused") final int id) { 450 | dialog.cancel(); 451 | } 452 | }); 453 | final AlertDialog alert = builder.create(); 454 | alert.show(); 455 | } 456 | else{ 457 | 458 | if(marker.getSnippet().equals("This is you")){ 459 | marker.hideInfoWindow(); 460 | } 461 | else{ 462 | resetSelectedMarker(); 463 | 464 | final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); 465 | builder.setMessage(marker.getSnippet()) 466 | .setCancelable(true) 467 | .setPositiveButton("Yes", new DialogInterface.OnClickListener() { 468 | public void onClick(@SuppressWarnings("unused") final DialogInterface dialog, @SuppressWarnings("unused") final int id) { 469 | mSelectedMarker = marker; 470 | calculateDirections(marker); 471 | } 472 | }) 473 | .setNegativeButton("No", new DialogInterface.OnClickListener() { 474 | public void onClick(final DialogInterface dialog, @SuppressWarnings("unused") final int id) { 475 | dialog.cancel(); 476 | } 477 | }); 478 | final AlertDialog alert = builder.create(); 479 | alert.show(); 480 | } 481 | } 482 | } 483 | 484 | private void resetSelectedMarker(){ 485 | if(mSelectedMarker != null){ 486 | mSelectedMarker.setVisible(true); 487 | mSelectedMarker = null; 488 | removeTripMarkers(); 489 | } 490 | } 491 | 492 | private void removeTripMarkers(){ 493 | for(Marker marker: mTripMarkers){ 494 | marker.remove(); 495 | } 496 | } 497 | 498 | 499 | /** 500 | * get duration and distance of route 501 | * @param userLocation 502 | */ 503 | private void calculateDurationAndDistance(UserLocation userLocation){ 504 | Log.d(TAG, "calculateDurationAndDistance: calculating duration and distance."); 505 | showProgressBar(); 506 | 507 | com.google.maps.model.LatLng destination = new com.google.maps.model.LatLng( 508 | userLocation.getGeo_point().getLatitude(), 509 | userLocation.getGeo_point().getLongitude() 510 | ); 511 | DistanceMatrixApiRequest matrix = new DistanceMatrixApiRequest(mGeoApiContext); 512 | 513 | matrix.origins(mUserPosition); 514 | matrix.destinations(destination).setCallback(new PendingResult.Callback() { 515 | @Override 516 | public void onResult(DistanceMatrix results) { 517 | Gson gson = new GsonBuilder().setPrettyPrinting().create(); 518 | Log.d(TAG, "onResult: destination addresses: " + gson.toJson(results.destinationAddresses)); 519 | Log.d(TAG, "onResult: origin addresses: " + gson.toJson(results.originAddresses)); 520 | 521 | for(DistanceMatrixRow row: results.rows){ 522 | for(DistanceMatrixElement element: row.elements){ 523 | Log.d(TAG, "onResult: element: " + element.toString()); 524 | } 525 | } 526 | hideProgressBar(); 527 | } 528 | 529 | @Override 530 | public void onFailure(Throwable e) { 531 | Log.e(TAG, "onFailure: " + e.getMessage() ); 532 | hideProgressBar(); 533 | } 534 | }); 535 | } 536 | 537 | private void calculateDirections(Marker marker){ 538 | Log.d(TAG, "calculateDirections: calculating directions."); 539 | 540 | com.google.maps.model.LatLng destination = new com.google.maps.model.LatLng( 541 | marker.getPosition().latitude, 542 | marker.getPosition().longitude 543 | ); 544 | DirectionsApiRequest directions = new DirectionsApiRequest(mGeoApiContext); 545 | 546 | directions.alternatives(true); 547 | directions.origin(mUserPosition); 548 | Log.d(TAG, "calculateDirections: destination: " + destination.toString()); 549 | directions.destination(destination).setCallback(new PendingResult.Callback() { 550 | @Override 551 | public void onResult(DirectionsResult result) { 552 | 553 | addPolyLinesToMap(result); 554 | 555 | hideProgressBar(); 556 | } 557 | 558 | @Override 559 | public void onFailure(Throwable e) { 560 | Log.e(TAG, "onFailure: " + e.getMessage() ); 561 | hideProgressBar(); 562 | } 563 | }); 564 | } 565 | 566 | 567 | private void addPolyLinesToMap(final DirectionsResult result){ 568 | new Handler(Looper.getMainLooper()).post(new Runnable() { 569 | @Override 570 | public void run() { 571 | Log.d(TAG, "run: result routes: " + result.routes.length); 572 | if(mPolyLinesData.size() > 0){ 573 | for(PolylineData polylineData: mPolyLinesData){ 574 | polylineData.getPolyline().remove(); 575 | } 576 | mPolyLinesData.clear(); 577 | mPolyLinesData = new ArrayList<>(); 578 | } 579 | 580 | double duration = 999999999; 581 | for(DirectionsRoute route: result.routes){ 582 | Log.d(TAG, "run: leg: " + route.legs[0].toString()); 583 | List decodedPath = PolylineEncoding.decode(route.overviewPolyline.getEncodedPath()); 584 | 585 | List newDecodedPath = new ArrayList<>(); 586 | 587 | // This loops through all the LatLng coordinates of ONE polyline. 588 | // Uncomment the log for a demonstration 589 | for(com.google.maps.model.LatLng latLng: decodedPath){ 590 | 591 | // Log.d(TAG, "run: latlng: " + latLng.toString()); 592 | 593 | newDecodedPath.add(new LatLng( 594 | latLng.lat, 595 | latLng.lng 596 | )); 597 | } 598 | Polyline polyline = mGoogleMap.addPolyline(new PolylineOptions().addAll(newDecodedPath)); 599 | polyline.setColor(ContextCompat.getColor(getActivity(), R.color.darkGrey)); 600 | polyline.setClickable(true); 601 | mPolyLinesData.add(new PolylineData(polyline, route.legs[0])); 602 | 603 | // highlight the fastest route and adjust camera 604 | double tempDuration = route.legs[0].duration.inSeconds; 605 | if(tempDuration < duration){ 606 | duration = tempDuration; 607 | onPolylineClick(polyline); 608 | zoomRoute(polyline.getPoints()); 609 | } 610 | 611 | // hide the selected marker while polylines are visible 612 | mSelectedMarker.setVisible(false); 613 | } 614 | } 615 | }); 616 | } 617 | 618 | 619 | public void zoomRoute(List lstLatLngRoute) { 620 | 621 | if (mGoogleMap == null || lstLatLngRoute == null || lstLatLngRoute.isEmpty()) return; 622 | 623 | LatLngBounds.Builder boundsBuilder = new LatLngBounds.Builder(); 624 | for (LatLng latLngPoint : lstLatLngRoute) 625 | boundsBuilder.include(latLngPoint); 626 | 627 | int routePadding = 120; 628 | LatLngBounds latLngBounds = boundsBuilder.build(); 629 | 630 | mGoogleMap.animateCamera( 631 | CameraUpdateFactory.newLatLngBounds(latLngBounds, routePadding), 632 | 600, 633 | null 634 | ); 635 | } 636 | 637 | 638 | 639 | @Override 640 | public void onPolylineClick(Polyline polyline) { 641 | 642 | int index = 0; 643 | for(PolylineData polylineData: mPolyLinesData){ 644 | index++; 645 | Log.d(TAG, "onPolylineClick: toString: " + polylineData.toString()); 646 | if(polyline.getId().equals(polylineData.getPolyline().getId())){ 647 | polylineData.getPolyline().setColor(ContextCompat.getColor(getActivity(), R.color.blue1)); 648 | polylineData.getPolyline().setZIndex(1); 649 | Log.d(TAG, "onPolylineClick: data: " + polylineData.getLeg().endLocation); 650 | 651 | LatLng endLocation = new LatLng( 652 | polylineData.getLeg().endLocation.lat, 653 | polylineData.getLeg().endLocation.lng 654 | ); 655 | 656 | Marker marker = mGoogleMap.addMarker(new MarkerOptions() 657 | .position(endLocation) 658 | .title("Trip #" + index) 659 | .snippet("Duration: " + polylineData.getLeg().duration 660 | + "\n" + "Distance: " + polylineData.getLeg().distance 661 | )); 662 | 663 | mTripMarkers.add(marker); 664 | 665 | marker.showInfoWindow(); 666 | } 667 | else{ 668 | polylineData.getPolyline().setColor(ContextCompat.getColor(getActivity(), R.color.darkGrey)); 669 | polylineData.getPolyline().setZIndex(0); 670 | } 671 | } 672 | } 673 | 674 | private void stopLocationUpdates(){ 675 | mHandler.removeCallbacks(mRunnable); 676 | } 677 | 678 | @Override 679 | public void onResume() { 680 | Log.d(TAG, "LifeCycle Event: onResume: called."); 681 | mMapView.onResume(); 682 | startUserLocationsRunnable(); // update user locations every 'LOCATION_UPDATE_INTERVAL' 683 | super.onResume(); 684 | } 685 | 686 | @Override 687 | public void onStart() { 688 | Log.d(TAG, "LifeCycle Event: onStart: called."); 689 | mMapView.onStart(); 690 | super.onStart(); 691 | } 692 | 693 | @Override 694 | public void onStop() { 695 | Log.d(TAG, "LifeCycle Event: onStop: called."); 696 | mMapView.onStop(); 697 | super.onStop(); 698 | } 699 | 700 | @Override 701 | public void onMapReady(GoogleMap map) { 702 | Log.d(TAG, "LifeCycle Event: onMapReady: called."); 703 | mGoogleMap = map; 704 | // initialMapAnimation(); 705 | addMapMarkers(); 706 | // mGoogleMap.setTrafficEnabled(true); 707 | mGoogleMap.setOnPolylineClickListener(this); 708 | } 709 | 710 | @Override 711 | public void onPause() { 712 | Log.d(TAG, "LifeCycle Event: onPause: called."); 713 | stopLocationUpdates(); // stop updating user locations 714 | super.onPause(); 715 | mMapView.onPause(); 716 | } 717 | 718 | @Override 719 | public void onDestroy() { 720 | Log.d(TAG, "LifeCycle Event: onDestroy: called."); 721 | super.onDestroy(); 722 | mMapView.onDestroy(); 723 | } 724 | 725 | @Override 726 | public void onLowMemory() { 727 | Log.d(TAG, "LifeCycle Event: onLowMemory: called."); 728 | mMapView.onLowMemory(); 729 | super.onLowMemory(); 730 | } 731 | 732 | 733 | private void hideSoftKeyboard(){ 734 | //Hide the soft keyboard 735 | InputMethodManager imm = (InputMethodManager)getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); 736 | imm.hideSoftInputFromWindow(getActivity().getCurrentFocus().getWindowToken(), 0); 737 | } 738 | 739 | private void showProgressBar(){ 740 | new Handler(Looper.getMainLooper()).post(new Runnable() { 741 | @Override 742 | public void run() { 743 | mProgressBar.setVisibility(View.VISIBLE); 744 | } 745 | }); 746 | } 747 | 748 | private void hideProgressBar(){ 749 | new Handler(Looper.getMainLooper()).post(new Runnable() { 750 | @Override 751 | public void run() { 752 | mProgressBar.setVisibility(View.GONE); 753 | } 754 | }); 755 | } 756 | 757 | private void initialMapAnimation(){ 758 | ViewWeightAnimationWrapper mapAnimationWrapper = new ViewWeightAnimationWrapper(mMapContainer); 759 | ObjectAnimator mapAnimation = ObjectAnimator.ofFloat(mapAnimationWrapper, 760 | "weight", 761 | 0, 762 | 50); 763 | mapAnimation.setDuration(800); 764 | } 765 | 766 | private void expandMapAnimation(){ 767 | ViewWeightAnimationWrapper mapAnimationWrapper = new ViewWeightAnimationWrapper(mMapContainer); 768 | ObjectAnimator mapAnimation = ObjectAnimator.ofFloat(mapAnimationWrapper, 769 | "weight", 770 | 50, 771 | 100); 772 | mapAnimation.setDuration(800); 773 | 774 | ViewWeightAnimationWrapper recyclerAnimationWrapper = new ViewWeightAnimationWrapper(mUserListRecyclerView); 775 | ObjectAnimator recyclerAnimation = ObjectAnimator.ofFloat(recyclerAnimationWrapper, 776 | "weight", 777 | 50, 778 | 0); 779 | recyclerAnimation.setDuration(800); 780 | 781 | recyclerAnimation.start(); 782 | mapAnimation.start(); 783 | } 784 | 785 | private void contractMapAnimation(){ 786 | ViewWeightAnimationWrapper mapAnimationWrapper = new ViewWeightAnimationWrapper(mMapContainer); 787 | ObjectAnimator mapAnimation = ObjectAnimator.ofFloat(mapAnimationWrapper, 788 | "weight", 789 | 100, 790 | 50); 791 | mapAnimation.setDuration(800); 792 | 793 | ViewWeightAnimationWrapper recyclerAnimationWrapper = new ViewWeightAnimationWrapper(mUserListRecyclerView); 794 | ObjectAnimator recyclerAnimation = ObjectAnimator.ofFloat(recyclerAnimationWrapper, 795 | "weight", 796 | 0, 797 | 50); 798 | recyclerAnimation.setDuration(800); 799 | 800 | recyclerAnimation.start(); 801 | mapAnimation.start(); 802 | } 803 | 804 | } 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | -------------------------------------------------------------------------------- /app/src/main/java/com/codingwithmitch/googledirectionstest/util/Check.java: -------------------------------------------------------------------------------- 1 | package com.codingwithmitch.googledirectionstest.util; 2 | 3 | public class Check { 4 | 5 | /** 6 | * Return true if the @param is null 7 | * @param string 8 | * @return 9 | */ 10 | public static boolean isEmpty(String string){ 11 | return string.equals(""); 12 | } 13 | 14 | /** 15 | * Return true if @param 's1' matches @param 's2' 16 | * @param s1 17 | * @param s2 18 | * @return 19 | */ 20 | public static boolean doStringsMatch(String s1, String s2){ 21 | return s1.equals(s2); 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/codingwithmitch/googledirectionstest/util/MyClusterManagerRenderer.java: -------------------------------------------------------------------------------- 1 | package com.codingwithmitch.googledirectionstest.util; 2 | 3 | import android.app.AlertDialog; 4 | import android.content.Context; 5 | import android.content.DialogInterface; 6 | import android.content.Intent; 7 | import android.graphics.Bitmap; 8 | import android.graphics.Color; 9 | import android.graphics.drawable.BitmapDrawable; 10 | import android.graphics.drawable.ColorDrawable; 11 | import android.graphics.drawable.Drawable; 12 | import android.net.Uri; 13 | import android.util.Log; 14 | import android.view.LayoutInflater; 15 | import android.view.View; 16 | import android.view.ViewGroup; 17 | import android.widget.ImageView; 18 | import android.widget.Toast; 19 | 20 | import com.bumptech.glide.Glide; 21 | import com.bumptech.glide.request.RequestOptions; 22 | import com.codingwithmitch.googledirectionstest.R; 23 | import com.codingwithmitch.googledirectionstest.models.ClusterMarker; 24 | import com.codingwithmitch.googledirectionstest.models.UserLocation; 25 | import com.google.android.gms.maps.GoogleMap; 26 | import com.google.android.gms.maps.model.BitmapDescriptorFactory; 27 | import com.google.android.gms.maps.model.Marker; 28 | import com.google.android.gms.maps.model.MarkerOptions; 29 | import com.google.firebase.auth.FirebaseAuth; 30 | import com.google.maps.android.clustering.Cluster; 31 | import com.google.maps.android.clustering.ClusterManager; 32 | import com.google.maps.android.clustering.view.DefaultClusterRenderer; 33 | import com.google.maps.android.ui.IconGenerator; 34 | 35 | import java.util.ArrayList; 36 | import java.util.Iterator; 37 | import java.util.List; 38 | 39 | public class MyClusterManagerRenderer extends DefaultClusterRenderer 40 | { 41 | 42 | private static final String TAG = "MyClusterManagerRendere"; 43 | 44 | private final IconGenerator iconGenerator; 45 | private final IconGenerator clusterIconGenerator; 46 | private final ImageView imageView; 47 | private final ImageView clusterImageView; 48 | private final int markerWidth; 49 | private final int markerHeight; 50 | private Context context; 51 | 52 | public MyClusterManagerRenderer(Context context, GoogleMap googleMap, 53 | ClusterManager clusterManager) { 54 | 55 | super(context, googleMap, clusterManager); 56 | 57 | this.context = context.getApplicationContext(); 58 | 59 | // initialize cluster icon generator 60 | clusterIconGenerator = new IconGenerator(context.getApplicationContext()); 61 | View clusterView = LayoutInflater.from(context).inflate(R.layout.layout_custom_marker, null); 62 | clusterIconGenerator.setContentView(clusterView); 63 | clusterImageView = clusterView.findViewById(R.id.profile_image); 64 | 65 | // initialize cluster item icon generator 66 | iconGenerator = new IconGenerator(context.getApplicationContext()); 67 | imageView = new ImageView(context.getApplicationContext()); 68 | markerWidth = (int) context.getResources().getDimension(R.dimen.custom_marker_image); 69 | markerHeight = (int) context.getResources().getDimension(R.dimen.custom_marker_image); 70 | imageView.setLayoutParams(new ViewGroup.LayoutParams(markerWidth, markerHeight)); 71 | int padding = (int) context.getResources().getDimension(R.dimen.custom_marker_padding); 72 | imageView.setPadding(padding, padding, padding, padding); 73 | iconGenerator.setContentView(imageView); 74 | 75 | } 76 | 77 | @Override 78 | protected void onBeforeClusterItemRendered(ClusterMarker item, MarkerOptions markerOptions) { 79 | 80 | imageView.setImageResource(item.getIconPicture()); 81 | Bitmap icon = iconGenerator.makeIcon(); 82 | markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon)).title(item.getTitle()); 83 | 84 | } 85 | 86 | @Override 87 | protected void onBeforeClusterRendered(Cluster cluster, MarkerOptions markerOptions) { 88 | 89 | Iterator iterator = cluster.getItems().iterator(); 90 | 91 | clusterImageView.setImageResource(iterator.next().getIconPicture()); 92 | Bitmap icon = clusterIconGenerator.makeIcon(String.valueOf(cluster.getSize())); 93 | markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon)); 94 | } 95 | 96 | public void setUpdateMarker(ClusterMarker clusterMarker) { 97 | Marker marker = getMarker(clusterMarker); 98 | if (marker != null) { 99 | marker.setPosition(clusterMarker.getPosition()); 100 | } 101 | } 102 | 103 | @Override 104 | protected boolean shouldRenderAsCluster(Cluster cluster) { 105 | return false; 106 | } 107 | } 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /app/src/main/java/com/codingwithmitch/googledirectionstest/util/ViewWeightAnimationWrapper.java: -------------------------------------------------------------------------------- 1 | package com.codingwithmitch.googledirectionstest.util; 2 | 3 | import android.view.View; 4 | import android.widget.LinearLayout; 5 | 6 | public class ViewWeightAnimationWrapper { 7 | private View view; 8 | 9 | public ViewWeightAnimationWrapper(View view) { 10 | if (view.getLayoutParams() instanceof LinearLayout.LayoutParams) { 11 | this.view = view; 12 | } else { 13 | throw new IllegalArgumentException("The view should have LinearLayout as parent"); 14 | } 15 | } 16 | 17 | public void setWeight(float weight) { 18 | LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) view.getLayoutParams(); 19 | params.weight = weight; 20 | view.getParent().requestLayout(); 21 | } 22 | 23 | public float getWeight() { 24 | return ((LinearLayout.LayoutParams) view.getLayoutParams()).weight; 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_in_down.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_in_up.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_out_down.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/anim/slide_out_up.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cartman_cop.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/GoogleMaps2018-Test/fbc16ca897ced730deffd96a7718c879e1a9c691/app/src/main/res/drawable/cartman_cop.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/chef.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/GoogleMaps2018-Test/fbc16ca897ced730deffd96a7718c879e1a9c691/app/src/main/res/drawable/chef.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/cwm_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/GoogleMaps2018-Test/fbc16ca897ced730deffd96a7718c879e1a9c691/app/src/main/res/drawable/cwm_logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/eric_cartman.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/GoogleMaps2018-Test/fbc16ca897ced730deffd96a7718c879e1a9c691/app/src/main/res/drawable/eric_cartman.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/grey_border_top.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_add_white_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_check_green_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_full_screen_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_refresh_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ike.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/GoogleMaps2018-Test/fbc16ca897ced730deffd96a7718c879e1a9c691/app/src/main/res/drawable/ike.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/kyle.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/GoogleMaps2018-Test/fbc16ca897ced730deffd96a7718c879e1a9c691/app/src/main/res/drawable/kyle.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/satan.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/GoogleMaps2018-Test/fbc16ca897ced730deffd96a7718c879e1a9c691/app/src/main/res/drawable/satan.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/theme_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/tweek.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/GoogleMaps2018-Test/fbc16ca897ced730deffd96a7718c879e1a9c691/app/src/main/res/drawable/tweek.jpg -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_chatroom.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 31 | 32 | 39 | 40 | 51 | 52 | 53 | 54 | 55 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_login.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | 12 | 19 | 20 | 21 | 29 | 30 | 31 | 40 | 41 |