diff --git a/app/build.gradle b/app/build.gradle index 113f182b5..0eb2276ef 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'com.android.application' +apply plugin: 'com.google.gms.google-services' repositories { mavenLocal() @@ -8,13 +9,13 @@ repositories { } android { - compileSdkVersion 24 - buildToolsVersion "24.0.1" + compileSdkVersion 28 + buildToolsVersion "29.0.0" defaultConfig { applicationId "com.google.firebase.udacity.friendlychat" minSdkVersion 16 - targetSdkVersion 24 + targetSdkVersion 28 versionCode 1 versionName "1.0" } @@ -32,12 +33,22 @@ android { } dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - testCompile 'junit:junit:4.12' + implementation fileTree(dir: 'libs', include: ['*.jar']) + implementation 'com.google.firebase:firebase-database:16.0.4' + implementation 'com.google.firebase:firebase-storage:16.0.4' + implementation 'com.google.firebase:firebase-auth:16.0.5' + implementation 'com.google.firebase:firebase-messaging:17.3.4' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' - compile 'com.android.support:design:24.2.0' - compile 'com.android.support:appcompat-v7:24.2.0' + + implementation 'androidx.appcompat:appcompat:1.1.0' // Displaying images - compile 'com.github.bumptech.glide:glide:3.6.1' -} \ No newline at end of file + implementation 'com.github.bumptech.glide:glide:4.10.0' + annotationProcessor 'com.github.bumptech.glide:compiler:4.10.0' + + // Firebase UI Implimentation + implementation 'com.firebaseui:firebase-ui-auth:4.3.1' + + +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index bcd0e213c..0ac0fbcb7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ + diff --git a/app/src/main/java/com/google/firebase/udacity/friendlychat/MainActivity.java b/app/src/main/java/com/google/firebase/udacity/friendlychat/MainActivity.java index b9b486cd1..69458cfbf 100644 --- a/app/src/main/java/com/google/firebase/udacity/friendlychat/MainActivity.java +++ b/app/src/main/java/com/google/firebase/udacity/friendlychat/MainActivity.java @@ -15,8 +15,9 @@ */ package com.google.firebase.udacity.friendlychat; +import android.content.Intent; +import android.net.Uri; import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; import android.text.Editable; import android.text.InputFilter; import android.text.TextWatcher; @@ -29,16 +30,43 @@ import android.widget.ImageButton; import android.widget.ListView; import android.widget.ProgressBar; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; + + +import com.firebase.ui.auth.AuthUI; +import com.firebase.ui.auth.IdpResponse; +import com.google.android.gms.tasks.Continuation; +import com.google.android.gms.tasks.OnCompleteListener; +import com.google.android.gms.tasks.OnSuccessListener; +import com.google.android.gms.tasks.Task; +import com.google.firebase.auth.FirebaseAuth; +import com.google.firebase.auth.FirebaseUser; +import com.google.firebase.database.ChildEventListener; +import com.google.firebase.database.DataSnapshot; +import com.google.firebase.database.DatabaseError; +import com.google.firebase.database.DatabaseReference; +import com.google.firebase.database.FirebaseDatabase; +import com.google.firebase.database.annotations.Nullable; +import com.google.firebase.storage.FirebaseStorage; +import com.google.firebase.storage.StorageReference; +import com.google.firebase.storage.UploadTask; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; public class MainActivity extends AppCompatActivity { - private static final String TAG = "MainActivity"; - public static final String ANONYMOUS = "anonymous"; public static final int DEFAULT_MSG_LENGTH_LIMIT = 1000; + private static final String TAG = "MainActivity"; + + + private static final int RC_SIGN_IN = 1; + private static final int RC_PHOTO_PICKER = 2; private ListView mMessageListView; private MessageAdapter mMessageAdapter; @@ -46,9 +74,16 @@ public class MainActivity extends AppCompatActivity { private ImageButton mPhotoPickerButton; private EditText mMessageEditText; private Button mSendButton; - private String mUsername; + private FirebaseDatabase mFirebaseDatabase; + private DatabaseReference mMessagesDatabaseReference; + private ChildEventListener mChildEventListener; + private FirebaseAuth mFirebaseAuth; + private FirebaseAuth.AuthStateListener mAuthStateListener; + private FirebaseStorage mFirebaseStorage; + private StorageReference mChatPhotosStorageReference; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -56,6 +91,16 @@ protected void onCreate(Bundle savedInstanceState) { mUsername = ANONYMOUS; + + // Initialize Firebase components + mFirebaseDatabase = FirebaseDatabase.getInstance(); + mFirebaseAuth = FirebaseAuth.getInstance(); + mFirebaseStorage = FirebaseStorage.getInstance(); + + mMessagesDatabaseReference = mFirebaseDatabase.getReference().child("messages"); + mChatPhotosStorageReference = mFirebaseStorage.getReference().child("chat_photos"); + + // Initialize references to views mProgressBar = (ProgressBar) findViewById(R.id.progressBar); mMessageListView = (ListView) findViewById(R.id.messageListView); @@ -75,7 +120,10 @@ protected void onCreate(Bundle savedInstanceState) { mPhotoPickerButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - // TODO: Fire an intent to show an image picker + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.setType("image/jpeg"); + intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true); + startActivityForResult(Intent.createChooser(intent, "Complete action using"), RC_PHOTO_PICKER); } }); @@ -105,11 +153,110 @@ public void afterTextChanged(Editable editable) { @Override public void onClick(View view) { // TODO: Send messages on click + // Send messages on click + FriendlyMessage friendlyMessage = new FriendlyMessage(mMessageEditText.getText().toString(), mUsername, null); + mMessagesDatabaseReference.push().setValue(friendlyMessage); // Clear input box mMessageEditText.setText(""); } }); + + + mAuthStateListener = new FirebaseAuth.AuthStateListener() { + @Override + public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) { + FirebaseUser user = firebaseAuth.getCurrentUser(); + if (user != null) { + onSignedInInitialize(user.getDisplayName()); + } else { + onSignOutCleanup(); + startActivityForResult( + AuthUI.getInstance() + .createSignInIntentBuilder() + .setIsSmartLockEnabled(false) + .setAvailableProviders(Arrays.asList( + new AuthUI.IdpConfig.GoogleBuilder().build(), + new AuthUI.IdpConfig.EmailBuilder().build(), + new AuthUI.IdpConfig.PhoneBuilder().build())) + .build(), + RC_SIGN_IN); + } + } + }; + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, @androidx.annotation.Nullable Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + + switch (requestCode) { + case RC_SIGN_IN: + if (resultCode == RESULT_OK) { + Toast.makeText(this, "Signed in!", Toast.LENGTH_SHORT).show(); + } else if (resultCode == RESULT_CANCELED) { + Toast.makeText(this, "Sign in canceled", Toast.LENGTH_SHORT).show(); + finish(); + } + break; + case RC_PHOTO_PICKER: + if (resultCode == RESULT_OK) { + Toast.makeText(this, "picker in!", Toast.LENGTH_SHORT).show(); + Uri selectedImageUri = data.getData(); + + // Get a reference to store file at chat_photos/ + final StorageReference photoRef = mChatPhotosStorageReference.child(selectedImageUri.getLastPathSegment()); + + // Upload file to Firebase Storage + photoRef.putFile(selectedImageUri) + .continueWithTask(new Continuation>() { + @Override + public Task then(@NonNull Task task) throws Exception { + if (!task.isSuccessful()) { + throw task.getException(); + } + + // Continue with the task to get the download URL + return photoRef.getDownloadUrl(); + } + }).addOnCompleteListener(new OnCompleteListener() { + @Override + public void onComplete(@NonNull Task task) { + if (task.isSuccessful()) { + Uri downloadUri = task.getResult(); + FriendlyMessage friendlyMessage = new FriendlyMessage(null, mUsername, downloadUri.toString()); + mMessagesDatabaseReference.push().setValue(friendlyMessage); + } else { + // Handle failures + // ... + } + } + }); + } + break; + + default: + Toast.makeText(this, "Something Wrong", Toast.LENGTH_SHORT).show(); + break; + } + } + + + @Override + protected void onResume() { + super.onResume(); + mFirebaseAuth.addAuthStateListener(mAuthStateListener); + } + + @Override + protected void onPause() { + super.onPause(); + if (mAuthStateListener != null) { + mFirebaseAuth.removeAuthStateListener(mAuthStateListener); + } + detachDatabaseReadListener(); + mMessageAdapter.clear(); } @Override @@ -121,6 +268,67 @@ public boolean onCreateOptionsMenu(Menu menu) { @Override public boolean onOptionsItemSelected(MenuItem item) { - return super.onOptionsItemSelected(item); + switch (item.getItemId()) { + case R.id.sign_out_menu: + AuthUI.getInstance().signOut(this); + return true; + + default: + return super.onOptionsItemSelected(item); + } + } + + private void onSignedInInitialize(String username) { + mUsername = username; + attachDatabaseReadListener(); + + } + + private void onSignOutCleanup() { + mUsername = ANONYMOUS; + mMessageAdapter.clear(); + detachDatabaseReadListener(); + + } + + + private void attachDatabaseReadListener() { + if (mChildEventListener == null) { + mChildEventListener = new ChildEventListener() { + @Override + public void onChildAdded(@NonNull DataSnapshot dataSnapshot, @Nullable String s) { + FriendlyMessage friendlyMessage = dataSnapshot.getValue(FriendlyMessage.class); + mMessageAdapter.add(friendlyMessage); + } + + @Override + public void onChildChanged(@NonNull DataSnapshot dataSnapshot, @Nullable String s) { + + } + + @Override + public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) { + + } + + @Override + public void onChildMoved(@NonNull DataSnapshot dataSnapshot, @Nullable String s) { + + } + + @Override + public void onCancelled(@NonNull DatabaseError databaseError) { + + } + }; + mMessagesDatabaseReference.addChildEventListener(mChildEventListener); + } + } + + private void detachDatabaseReadListener() { + if (mChildEventListener != null) { + mMessagesDatabaseReference.removeEventListener(mChildEventListener); + mChildEventListener = null; + } } -} +} \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8bf7fbac7..a4dac53e2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2,4 +2,6 @@ Friendly Chat Sign Out Send + + diff --git a/build.gradle b/build.gradle index 67fa30cbc..cded88118 100644 --- a/build.gradle +++ b/build.gradle @@ -4,12 +4,17 @@ buildscript { repositories { jcenter() mavenLocal() + google() } dependencies { - classpath 'com.android.tools.build:gradle:2.2.2' + classpath 'com.android.tools.build:gradle:3.5.0' + classpath "io.realm:realm-gradle-plugin:6.0.0" + classpath 'com.google.firebase:firebase-auth:16.0.5' + classpath 'com.google.gms:google-services:4.3.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files + } } @@ -23,3 +28,12 @@ allprojects { task clean(type: Delete) { delete rootProject.buildDir } + + +allprojects { + repositories { + jcenter() + maven { url 'https://maven.google.com' } + google() + } +} diff --git a/gradle.properties b/gradle.properties index 1d3591c8a..915f0e66f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,4 +15,6 @@ # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects -# org.gradle.parallel=true \ No newline at end of file +# org.gradle.parallel=true +android.enableJetifier=true +android.useAndroidX=true \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ce88022b7..f0be5ac14 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Tue Aug 16 17:43:49 PDT 2016 +#Wed Oct 16 11:08:18 BDT 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip