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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
379 changes: 379 additions & 0 deletions MainActivity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,379 @@
/**
* Copyright Google Inc. All Rights Reserved.
* <p/>
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.firebase.udacity.friendlychat;

import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.text.Editable;
import android.text.InputFilter;
import android.text.TextWatcher;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
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.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
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.remoteconfig.FirebaseRemoteConfig;
import com.google.firebase.remoteconfig.FirebaseRemoteConfigSettings;
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.HashMap;
import java.util.List;
import java.util.Map;

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;
public static final String FRIENDLY_MSG_LENGTH_KEY = "friendly_msg_length";

public static final int RC_SIGN_IN = 1;
private static final int RC_PHOTO_PICKER = 2;

private ListView mMessageListView;
private MessageAdapter mMessageAdapter;
private ProgressBar mProgressBar;
private ImageButton mPhotoPickerButton;
private EditText mMessageEditText;
private Button mSendButton;

private String mUsername;

// Firebase instance variables
private FirebaseDatabase mFirebaseDatabase;
private DatabaseReference mMessagesDatabaseReference;
private ChildEventListener mChildEventListener;
private FirebaseAuth mFirebaseAuth;
private FirebaseAuth.AuthStateListener mAuthStateListener;
private FirebaseStorage mFirebaseStorage;
private StorageReference mChatPhotosStorageReference;
private FirebaseRemoteConfig mFirebaseRemoteConfig;
private Uri downloadUrl;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

mUsername = ANONYMOUS;

// Initialize Firebase components
mFirebaseDatabase = FirebaseDatabase.getInstance();
mFirebaseAuth = FirebaseAuth.getInstance();
mFirebaseStorage = FirebaseStorage.getInstance();
mFirebaseRemoteConfig = FirebaseRemoteConfig.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);
mPhotoPickerButton = (ImageButton) findViewById(R.id.photoPickerButton);
mMessageEditText = (EditText) findViewById(R.id.messageEditText);
mSendButton = (Button) findViewById(R.id.sendButton);

// Initialize message ListView and its adapter
List<FriendlyMessage> friendlyMessages = new ArrayList<>();
mMessageAdapter = new MessageAdapter(this, R.layout.item_message, friendlyMessages);
mMessageListView.setAdapter(mMessageAdapter);

// Initialize provider list
List<AuthUI.IdpConfig> providers;
providers = new ArrayList<>();

// Initialize progress bar
mProgressBar.setVisibility(ProgressBar.INVISIBLE);

// ImagePickerButton shows an image picker to upload a image for a message
mPhotoPickerButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
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);
}
});

// Enable Send button when there's text to send
mMessageEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}

@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
if (charSequence.toString().trim().length() > 0) {
mSendButton.setEnabled(true);
} else {
mSendButton.setEnabled(false);
}
}

@Override
public void afterTextChanged(Editable editable) {
}
});
mMessageEditText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(DEFAULT_MSG_LENGTH_LIMIT)});

// Send button sends a message and clears the EditText
mSendButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
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) {
// User is signed in
onSignedInInitialize(user.getDisplayName());
} else {
// User is signed out
// deprecated method .setProviders has been replaced follwing example code
// https://github.com/firebase/FirebaseUI-Android/tree/master/auth#adding-providers
onSignedOutCleanup();
startActivityForResult(
AuthUI.getInstance()
.createSignInIntentBuilder()
.setIsSmartLockEnabled(false)
.setAvailableProviders(Arrays.asList(
new AuthUI.IdpConfig.GoogleBuilder().build(),
new AuthUI.IdpConfig.EmailBuilder().build()))
.build(),
RC_SIGN_IN);
}
}
};

// Create Remote Config Setting to enable developer mode.
// Fetching configs from the server is normally limited to 5 requests per hour.
// Enabling developer mode allows many more requests to be made per hour, so developers
// can test different config values during development.
FirebaseRemoteConfigSettings configSettings = new FirebaseRemoteConfigSettings.Builder()
.setDeveloperModeEnabled(BuildConfig.DEBUG)
.build();
mFirebaseRemoteConfig.setConfigSettings(configSettings);

// Define default config values. Defaults are used when fetched config values are not
// available. Eg: if an error occurred fetching values from the server.
Map<String, Object> defaultConfigMap = new HashMap<>();
defaultConfigMap.put(FRIENDLY_MSG_LENGTH_KEY, DEFAULT_MSG_LENGTH_LIMIT);
mFirebaseRemoteConfig.setDefaults(defaultConfigMap);
fetchConfig();
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_SIGN_IN) {
if (resultCode == RESULT_OK) {
// Sign-in succeeded, set up the UI
Toast.makeText(this, "Signed in!", Toast.LENGTH_SHORT).show();
} else if (resultCode == RESULT_CANCELED) {
// Sign in was canceled by the user, finish the activity
Toast.makeText(this, "Sign in canceled", Toast.LENGTH_SHORT).show();
finish();
}
} else if (requestCode == RC_PHOTO_PICKER && resultCode == RESULT_OK) {
Uri selectedImageUri = data.getData();

// Get a reference to store file at chat_photos/<FILENAME>
final StorageReference photoRef = mChatPhotosStorageReference.child(selectedImageUri.getLastPathSegment());

// Upload file to Firebase Storage
photoRef.putFile(selectedImageUri)
.addOnSuccessListener(this, new OnSuccessListener<UploadTask.TaskSnapshot>() {
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
// When the image has successfully uploaded, we get its download URL
// DONE fix deprecated method error .getDownloadUrl()
// .getDownloadUrl() method is deprecated
// Original example code-
// Uri downloadUrl = taskSnapshot.getDownloadUrl();
// Code snippet from stackoverflow-
// https://stackoverflow.com/a/50743192/8811523

photoRef.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
@Override
public void onSuccess(Uri uri) {downloadUrl = uri;}
});

// Set the download URL to the message box, so that the user can send it
// to the database

FriendlyMessage friendlyMessage = new FriendlyMessage(null, mUsername,
downloadUrl.toString());
mMessagesDatabaseReference.push().setValue(friendlyMessage);
}
});
}
}

@Override
protected void onResume() {
super.onResume();
mFirebaseAuth.addAuthStateListener(mAuthStateListener);
}

@Override
protected void onPause() {
super.onPause();
if (mAuthStateListener != null) {
mFirebaseAuth.removeAuthStateListener(mAuthStateListener);
}
mMessageAdapter.clear();
detachDatabaseReadListener();
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main_menu, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem 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 onSignedOutCleanup() {
mUsername = ANONYMOUS;
mMessageAdapter.clear();
detachDatabaseReadListener();
}

private void attachDatabaseReadListener() {
if (mChildEventListener == null) {
mChildEventListener = new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
FriendlyMessage friendlyMessage = dataSnapshot.getValue(FriendlyMessage.class);
mMessageAdapter.add(friendlyMessage);
}

public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}

public void onChildRemoved(DataSnapshot dataSnapshot) {
}

public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}

public void onCancelled(DatabaseError databaseError) {
}
};
mMessagesDatabaseReference.addChildEventListener(mChildEventListener);
}
}

private void detachDatabaseReadListener() {
if (mChildEventListener != null) {
mMessagesDatabaseReference.removeEventListener(mChildEventListener);
mChildEventListener = null;
}
}

// Fetch the config to determine the allowed length of messages.
public void fetchConfig() {
long cacheExpiration = 3600; // 1 hour in seconds
// If developer mode is enabled reduce cacheExpiration to 0 so that each fetch goes to the
// server. This should not be used in release builds.
if (mFirebaseRemoteConfig.getInfo().getConfigSettings().isDeveloperModeEnabled()) {
cacheExpiration = 0;
}
mFirebaseRemoteConfig.fetch(cacheExpiration)
.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
// Make the fetched config available
// via FirebaseRemoteConfig get<type> calls, e.g., getLong, getString.
mFirebaseRemoteConfig.activateFetched();

// Update the EditText length limit with
// the newly retrieved values from Remote Config.
applyRetrievedLengthLimit();
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
// An error occurred when fetching the config.
Log.w(TAG, "Error fetching config", e);

// Update the EditText length limit with
// the newly retrieved values from Remote Config.
applyRetrievedLengthLimit();
}
});
}

/**
* Apply retrieved length limit to edit text field. This result may be fresh from the server or it may be from
* cached values.
*/
private void applyRetrievedLengthLimit() {
Long friendly_msg_length = mFirebaseRemoteConfig.getLong(FRIENDLY_MSG_LENGTH_KEY);
mMessageEditText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(friendly_msg_length.intValue())});
Log.d(TAG, FRIENDLY_MSG_LENGTH_KEY + " = " + friendly_msg_length);
}
}
Loading