├── .gitignore ├── .idea ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── app ├── .gitignore ├── build.gradle ├── google-services.json ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── codingwithmitch │ │ └── com │ │ └── firestoregettingstarted │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── codingwithmitch │ │ │ └── com │ │ │ └── firestoregettingstarted │ │ │ ├── IMainActivity.java │ │ │ ├── LoginActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── NewNoteDialog.java │ │ │ ├── NoteRecyclerViewAdapter.java │ │ │ ├── ViewNoteDialog.java │ │ │ └── models │ │ │ └── Note.java │ └── res │ │ ├── drawable-hdpi │ │ ├── ic_new_note.png │ │ └── ic_plus_white.png │ │ ├── drawable-mdpi │ │ ├── ic_new_note.png │ │ └── ic_plus_white.png │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable-xhdpi │ │ ├── ic_new_note.png │ │ └── ic_plus_white.png │ │ ├── drawable-xxhdpi │ │ ├── ic_new_note.png │ │ └── ic_plus_white.png │ │ ├── drawable │ │ ├── firestore.jpeg │ │ ├── grey_border.xml │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── activity_login.xml │ │ ├── activity_main.xml │ │ ├── dialog_new_note.xml │ │ ├── dialog_view_note.xml │ │ └── layout_note_list_item.xml │ │ ├── menu │ │ └── options_menu.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 │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── codingwithmitch │ └── com │ └── firestoregettingstarted │ └── 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/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 27 5 | defaultConfig { 6 | applicationId "codingwithmitch.com.firestoregettingstarted" 7 | minSdkVersion 16 8 | targetSdkVersion 27 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | buildToolsVersion '27.0.3' 20 | } 21 | 22 | dependencies { 23 | implementation fileTree(include: ['*.jar'], dir: 'libs') 24 | implementation 'com.android.support:appcompat-v7:27.1.1' 25 | implementation 'com.android.support.constraint:constraint-layout:1.1.0' 26 | testImplementation 'junit:junit:4.12' 27 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 28 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 29 | //Android Percent Support Library 30 | implementation 'com.android.support:percent:27.1.1' 31 | //Android Support Design Library 32 | implementation 'com.android.support:design:27.1.1' 33 | // Firestore 34 | implementation 'com.google.firebase:firebase-firestore:12.0.1' 35 | //Firebase Authentication 36 | implementation 'com.google.firebase:firebase-auth:12.0.1' 37 | } 38 | apply plugin: 'com.google.gms.google-services' -------------------------------------------------------------------------------- /app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "702340596069", 4 | "firebase_url": "https://firestoregettingstarted.firebaseio.com", 5 | "project_id": "firestoregettingstarted", 6 | "storage_bucket": "firestoregettingstarted.appspot.com" 7 | }, 8 | "client": [ 9 | { 10 | "client_info": { 11 | "mobilesdk_app_id": "1:702340596069:android:e9d06858bfcef3ec", 12 | "android_client_info": { 13 | "package_name": "codingwithmitch.com.firestoregettingstarted" 14 | } 15 | }, 16 | "oauth_client": [ 17 | { 18 | "client_id": "702340596069-0k73q9uclqflnaoduk2ltmopn06pmv71.apps.googleusercontent.com", 19 | "client_type": 3 20 | } 21 | ], 22 | "api_key": [ 23 | { 24 | "current_key": "AIzaSyCvBrvEMFRNlv9q0Oz7UjcGAIBN1dgbg5Y" 25 | } 26 | ], 27 | "services": { 28 | "analytics_service": { 29 | "status": 1 30 | }, 31 | "appinvite_service": { 32 | "status": 1, 33 | "other_platform_oauth_client": [] 34 | }, 35 | "ads_service": { 36 | "status": 2 37 | } 38 | } 39 | } 40 | ], 41 | "configuration_version": "1" 42 | } -------------------------------------------------------------------------------- /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/codingwithmitch/com/firestoregettingstarted/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package codingwithmitch.com.firestoregettingstarted; 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() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("codingwithmitch.com.firestoregettingstarted", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/java/codingwithmitch/com/firestoregettingstarted/IMainActivity.java: -------------------------------------------------------------------------------- 1 | package codingwithmitch.com.firestoregettingstarted; 2 | 3 | import codingwithmitch.com.firestoregettingstarted.models.Note; 4 | 5 | /** 6 | * Created by User on 5/14/2018. 7 | */ 8 | 9 | public interface IMainActivity { 10 | 11 | void createNewNote(String title, String content); 12 | 13 | void updateNote(Note note); 14 | 15 | void onNoteSelected(Note note); 16 | 17 | void deleteNote(Note note); 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/codingwithmitch/com/firestoregettingstarted/LoginActivity.java: -------------------------------------------------------------------------------- 1 | package codingwithmitch.com.firestoregettingstarted; 2 | 3 | import android.app.Dialog; 4 | import android.content.Intent; 5 | import android.support.annotation.NonNull; 6 | import android.support.design.widget.Snackbar; 7 | import android.support.v7.app.AppCompatActivity; 8 | import android.os.Bundle; 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.google.android.gms.common.ConnectionResult; 18 | import com.google.android.gms.common.GoogleApiAvailability; 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 | 26 | public class LoginActivity extends AppCompatActivity implements View.OnClickListener{ 27 | 28 | private static final String TAG = "LoginActivity"; 29 | 30 | private static final int ERROR_DIALOG_REQUEST = 9001; 31 | 32 | //Firebase 33 | private FirebaseAuth.AuthStateListener mAuthListener; 34 | 35 | // widgets 36 | private EditText mEmail, mPassword; 37 | private ProgressBar mProgressBar; 38 | private Button mSignIn; 39 | 40 | 41 | @Override 42 | protected void onCreate(Bundle savedInstanceState) { 43 | super.onCreate(savedInstanceState); 44 | setContentView(R.layout.activity_login); 45 | mEmail = findViewById(R.id.email); 46 | mPassword = findViewById(R.id.password); 47 | mProgressBar = findViewById(R.id.progressBar); 48 | mSignIn = findViewById(R.id.email_sign_in_button); 49 | 50 | setupFirebaseAuth(); 51 | if(servicesOK()){ 52 | mSignIn.setOnClickListener(this); 53 | } 54 | hideSoftKeyboard(); 55 | } 56 | 57 | @Override 58 | public void onClick(View view) { 59 | 60 | if(view.getId() == R.id.email_sign_in_button){ 61 | //check if the fields are filled out 62 | if(!isEmpty(mEmail.getText().toString()) 63 | && !isEmpty(mPassword.getText().toString())){ 64 | Log.d(TAG, "onClick: attempting to authenticate."); 65 | 66 | showDialog(); 67 | 68 | FirebaseAuth.getInstance().signInWithEmailAndPassword(mEmail.getText().toString(), 69 | mPassword.getText().toString()) 70 | .addOnCompleteListener(new OnCompleteListener() { 71 | @Override 72 | public void onComplete(@NonNull Task task) { 73 | 74 | hideDialog(); 75 | 76 | } 77 | }).addOnFailureListener(new OnFailureListener() { 78 | @Override 79 | public void onFailure(@NonNull Exception e) { 80 | Snackbar.make(getCurrentFocus().getRootView(), "Authentication Failed", Snackbar.LENGTH_SHORT).show(); 81 | hideDialog(); 82 | } 83 | }); 84 | }else{ 85 | Snackbar.make(getCurrentFocus().getRootView(), "Fill in all the fields", Snackbar.LENGTH_SHORT).show(); 86 | } 87 | } 88 | } 89 | 90 | public boolean servicesOK(){ 91 | Log.d(TAG, "servicesOK: Checking Google Services."); 92 | 93 | int isAvailable = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(LoginActivity.this); 94 | 95 | if(isAvailable == ConnectionResult.SUCCESS){ 96 | //everything is ok and the user can make mapping requests 97 | Log.d(TAG, "servicesOK: Play Services is OK"); 98 | return true; 99 | } 100 | else if(GoogleApiAvailability.getInstance().isUserResolvableError(isAvailable)){ 101 | //an error occured, but it's resolvable 102 | Log.d(TAG, "servicesOK: an error occured, but it's resolvable."); 103 | Dialog dialog = GoogleApiAvailability.getInstance().getErrorDialog(LoginActivity.this, isAvailable, ERROR_DIALOG_REQUEST); 104 | dialog.show(); 105 | } 106 | else{ 107 | Toast.makeText(this, "Can't connect to services", Toast.LENGTH_SHORT).show(); 108 | } 109 | 110 | return false; 111 | } 112 | 113 | /** 114 | * Return true if the @param is null 115 | * @param string 116 | * @return 117 | */ 118 | private boolean isEmpty(String string){ 119 | return string.equals(""); 120 | } 121 | 122 | 123 | private void showDialog(){ 124 | mProgressBar.setVisibility(View.VISIBLE); 125 | 126 | } 127 | 128 | private void hideDialog(){ 129 | if(mProgressBar.getVisibility() == View.VISIBLE){ 130 | mProgressBar.setVisibility(View.INVISIBLE); 131 | } 132 | } 133 | 134 | private void hideSoftKeyboard(){ 135 | this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); 136 | } 137 | 138 | /* 139 | ----------------------------- Firebase setup --------------------------------- 140 | */ 141 | private void setupFirebaseAuth(){ 142 | Log.d(TAG, "setupFirebaseAuth: started."); 143 | 144 | mAuthListener = new FirebaseAuth.AuthStateListener() { 145 | @Override 146 | public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) { 147 | FirebaseUser user = firebaseAuth.getCurrentUser(); 148 | if (user != null) { 149 | 150 | Log.d(TAG, "onAuthStateChanged:signed_in:" + user.getUid()); 151 | Toast.makeText(LoginActivity.this, "Signed in", Toast.LENGTH_SHORT).show(); 152 | 153 | Intent intent = new Intent(LoginActivity.this, MainActivity.class); 154 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 155 | startActivity(intent); 156 | finish(); 157 | 158 | 159 | } else { 160 | // User is signed out 161 | Log.d(TAG, "onAuthStateChanged:signed_out"); 162 | } 163 | } 164 | }; 165 | } 166 | 167 | @Override 168 | public void onStart() { 169 | super.onStart(); 170 | FirebaseAuth.getInstance().addAuthStateListener(mAuthListener); 171 | } 172 | 173 | @Override 174 | public void onStop() { 175 | super.onStop(); 176 | if (mAuthListener != null) { 177 | FirebaseAuth.getInstance().removeAuthStateListener(mAuthListener); 178 | } 179 | } 180 | 181 | 182 | } 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | -------------------------------------------------------------------------------- /app/src/main/java/codingwithmitch/com/firestoregettingstarted/MainActivity.java: -------------------------------------------------------------------------------- 1 | package codingwithmitch.com.firestoregettingstarted; 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.FloatingActionButton; 8 | import android.support.design.widget.Snackbar; 9 | import android.support.v4.widget.SwipeRefreshLayout; 10 | import android.support.v7.app.AppCompatActivity; 11 | import android.support.v7.widget.LinearLayoutManager; 12 | import android.support.v7.widget.RecyclerView; 13 | import android.util.Log; 14 | import android.view.Menu; 15 | import android.view.MenuItem; 16 | import android.view.View; 17 | import android.widget.Toast; 18 | 19 | import com.google.android.gms.tasks.OnCompleteListener; 20 | import com.google.android.gms.tasks.Task; 21 | import com.google.firebase.auth.FirebaseAuth; 22 | import com.google.firebase.auth.FirebaseUser; 23 | import com.google.firebase.firestore.CollectionReference; 24 | import com.google.firebase.firestore.DocumentReference; 25 | import com.google.firebase.firestore.DocumentSnapshot; 26 | import com.google.firebase.firestore.FirebaseFirestore; 27 | import com.google.firebase.firestore.Query; 28 | import com.google.firebase.firestore.QueryDocumentSnapshot; 29 | import com.google.firebase.firestore.QuerySnapshot; 30 | 31 | import java.util.ArrayList; 32 | import java.util.HashMap; 33 | 34 | import codingwithmitch.com.firestoregettingstarted.models.Note; 35 | 36 | /** 37 | * Created by User on 5/14/2018. 38 | */ 39 | 40 | public class MainActivity extends AppCompatActivity implements 41 | View.OnClickListener, 42 | IMainActivity, 43 | SwipeRefreshLayout.OnRefreshListener 44 | { 45 | 46 | private static final String TAG = "MainActivity"; 47 | 48 | //Firebase 49 | private FirebaseAuth.AuthStateListener mAuthListener; 50 | 51 | 52 | //widgets 53 | private FloatingActionButton mFab; 54 | private RecyclerView mRecyclerView; 55 | private SwipeRefreshLayout mSwipeRefreshLayout; 56 | 57 | //vars 58 | private View mParentLayout; 59 | private ArrayList mNotes = new ArrayList<>(); 60 | private NoteRecyclerViewAdapter mNoteRecyclerViewAdapter; 61 | private DocumentSnapshot mLastQueriedDocument; 62 | 63 | 64 | @Override 65 | protected void onCreate(@Nullable Bundle savedInstanceState) { 66 | super.onCreate(savedInstanceState); 67 | setContentView(R.layout.activity_main); 68 | mFab = findViewById(R.id.fab); 69 | mParentLayout = findViewById(android.R.id.content); 70 | mRecyclerView = findViewById(R.id.recycler_view); 71 | mSwipeRefreshLayout = findViewById(R.id.swipe_refresh_layout); 72 | 73 | mFab.setOnClickListener(this); 74 | mSwipeRefreshLayout.setOnRefreshListener(this); 75 | 76 | setupFirebaseAuth(); 77 | initRecyclerView(); 78 | getNotes(); 79 | } 80 | 81 | @Override 82 | public void deleteNote(final Note note){ 83 | FirebaseFirestore db = FirebaseFirestore.getInstance(); 84 | 85 | DocumentReference noteRef = db 86 | .collection("notes") 87 | .document(note.getNote_id()); 88 | 89 | noteRef.delete().addOnCompleteListener(new OnCompleteListener() { 90 | @Override 91 | public void onComplete(@NonNull Task task) { 92 | if(task.isSuccessful()){ 93 | makeSnackBarMessage("Deleted note"); 94 | mNoteRecyclerViewAdapter.removeNote(note); 95 | } 96 | else{ 97 | makeSnackBarMessage("Failed. Check log."); 98 | } 99 | } 100 | }); 101 | } 102 | 103 | @Override 104 | public void onRefresh() { 105 | getNotes(); 106 | mSwipeRefreshLayout.setRefreshing(false); 107 | } 108 | 109 | private void getNotes(){ 110 | 111 | FirebaseFirestore db = FirebaseFirestore.getInstance(); 112 | 113 | CollectionReference notesCollectionRef = db 114 | .collection("notes"); 115 | 116 | Query notesQuery = null; 117 | if(mLastQueriedDocument != null){ 118 | notesQuery = notesCollectionRef 119 | .whereEqualTo("user_id", FirebaseAuth.getInstance().getCurrentUser().getUid()) 120 | .orderBy("timestamp", Query.Direction.ASCENDING) 121 | .startAfter(mLastQueriedDocument); 122 | } 123 | else{ 124 | notesQuery = notesCollectionRef 125 | .whereEqualTo("user_id", FirebaseAuth.getInstance().getCurrentUser().getUid()) 126 | .orderBy("timestamp", Query.Direction.ASCENDING); 127 | } 128 | 129 | notesQuery.get().addOnCompleteListener(new OnCompleteListener() { 130 | @Override 131 | public void onComplete(@NonNull Task task) { 132 | if(task.isSuccessful()){ 133 | 134 | for(QueryDocumentSnapshot document: task.getResult()){ 135 | Note note = document.toObject(Note.class); 136 | mNotes.add(note); 137 | // Log.d(TAG, "onComplete: got a new note. Position: " + (mNotes.size() - 1)); 138 | } 139 | 140 | if(task.getResult().size() != 0){ 141 | mLastQueriedDocument = task.getResult().getDocuments() 142 | .get(task.getResult().size() -1); 143 | } 144 | 145 | mNoteRecyclerViewAdapter.notifyDataSetChanged(); 146 | } 147 | else{ 148 | makeSnackBarMessage("Query Failed. Check Logs."); 149 | } 150 | } 151 | }); 152 | } 153 | 154 | private void initRecyclerView(){ 155 | if(mNoteRecyclerViewAdapter == null){ 156 | mNoteRecyclerViewAdapter = new NoteRecyclerViewAdapter(this, mNotes); 157 | } 158 | mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); 159 | mRecyclerView.setAdapter(mNoteRecyclerViewAdapter); 160 | } 161 | 162 | @Override 163 | public void updateNote(final Note note){ 164 | 165 | FirebaseFirestore db = FirebaseFirestore.getInstance(); 166 | 167 | DocumentReference noteRef = db 168 | .collection("notes") 169 | .document(note.getNote_id()); 170 | 171 | noteRef.update( 172 | "title", note.getTitle(), 173 | "content", note.getContent() 174 | ).addOnCompleteListener(new OnCompleteListener() { 175 | @Override 176 | public void onComplete(@NonNull Task task) { 177 | if(task.isSuccessful()){ 178 | makeSnackBarMessage("Updated note"); 179 | mNoteRecyclerViewAdapter.updateNote(note); 180 | } 181 | else{ 182 | makeSnackBarMessage("Failed. Check log."); 183 | } 184 | } 185 | }); 186 | } 187 | 188 | @Override 189 | public void onNoteSelected(Note note) { 190 | ViewNoteDialog dialog = ViewNoteDialog.newInstance(note); 191 | dialog.show(getSupportFragmentManager(), getString(R.string.dialog_view_note)); 192 | } 193 | 194 | @Override 195 | public void createNewNote(String title, String content) { 196 | 197 | FirebaseFirestore db = FirebaseFirestore.getInstance(); 198 | 199 | String userId = FirebaseAuth.getInstance().getCurrentUser().getUid(); 200 | 201 | DocumentReference newNoteRef = db 202 | .collection("notes") 203 | .document(); 204 | 205 | Note note = new Note(); 206 | note.setTitle(title); 207 | note.setContent(content); 208 | note.setNote_id(newNoteRef.getId()); 209 | note.setUser_id(userId); 210 | 211 | newNoteRef.set(note).addOnCompleteListener(new OnCompleteListener() { 212 | @Override 213 | public void onComplete(@NonNull Task task) { 214 | if(task.isSuccessful()){ 215 | makeSnackBarMessage("Created new note"); 216 | getNotes(); 217 | } 218 | else{ 219 | makeSnackBarMessage("Failed. Check log."); 220 | } 221 | } 222 | }); 223 | } 224 | 225 | private void makeSnackBarMessage(String message){ 226 | Snackbar.make(mParentLayout, message, Snackbar.LENGTH_SHORT).show(); 227 | } 228 | 229 | 230 | @Override 231 | public void onClick(View view) { 232 | 233 | switch (view.getId()){ 234 | 235 | case R.id.fab:{ 236 | //create a new note 237 | NewNoteDialog dialog = new NewNoteDialog(); 238 | dialog.show(getSupportFragmentManager(), getString(R.string.dialog_new_note)); 239 | break; 240 | } 241 | } 242 | } 243 | 244 | @Override 245 | public boolean onCreateOptionsMenu(Menu menu) { 246 | getMenuInflater().inflate(R.menu.options_menu, menu); 247 | 248 | return true; 249 | } 250 | 251 | @Override 252 | public boolean onOptionsItemSelected(MenuItem item) { 253 | switch (item.getItemId()){ 254 | case R.id.optionSignOut: 255 | signOut(); 256 | return true; 257 | 258 | default: 259 | return super.onOptionsItemSelected(item); 260 | } 261 | } 262 | 263 | private void signOut(){ 264 | Log.d(TAG, "signOut: signing out"); 265 | FirebaseAuth.getInstance().signOut(); 266 | } 267 | 268 | /* 269 | ----------------------------- Firebase setup --------------------------------- 270 | */ 271 | private void setupFirebaseAuth(){ 272 | Log.d(TAG, "setupFirebaseAuth: started."); 273 | 274 | mAuthListener = new FirebaseAuth.AuthStateListener() { 275 | @Override 276 | public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) { 277 | FirebaseUser user = firebaseAuth.getCurrentUser(); 278 | if (user != null) { 279 | 280 | Log.d(TAG, "onAuthStateChanged:signed_in:" + user.getUid()); 281 | 282 | } else { 283 | Log.d(TAG, "onAuthStateChanged:signed_out"); 284 | Intent intent = new Intent(MainActivity.this, LoginActivity.class); 285 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); 286 | startActivity(intent); 287 | finish(); 288 | } 289 | } 290 | }; 291 | } 292 | 293 | @Override 294 | public void onStart() { 295 | super.onStart(); 296 | FirebaseAuth.getInstance().addAuthStateListener(mAuthListener); 297 | } 298 | 299 | @Override 300 | public void onStop() { 301 | super.onStop(); 302 | if (mAuthListener != null) { 303 | FirebaseAuth.getInstance().removeAuthStateListener(mAuthListener); 304 | } 305 | } 306 | 307 | 308 | 309 | } 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | -------------------------------------------------------------------------------- /app/src/main/java/codingwithmitch/com/firestoregettingstarted/NewNoteDialog.java: -------------------------------------------------------------------------------- 1 | package codingwithmitch.com.firestoregettingstarted; 2 | 3 | import android.content.Context; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.support.design.widget.Snackbar; 7 | import android.support.v4.app.DialogFragment; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.widget.EditText; 12 | import android.widget.TextView; 13 | import android.widget.Toast; 14 | 15 | /** 16 | * Created by User on 5/14/2018. 17 | */ 18 | 19 | public class NewNoteDialog extends DialogFragment implements View.OnClickListener{ 20 | 21 | private static final String TAG = "NewNoteDialog"; 22 | 23 | //widgets 24 | private EditText mTitle, mContent; 25 | private TextView mCreate, mCancel; 26 | 27 | //vars 28 | private IMainActivity mIMainActivity; 29 | 30 | @Override 31 | public void onCreate(@Nullable Bundle savedInstanceState) { 32 | super.onCreate(savedInstanceState); 33 | 34 | int style = DialogFragment.STYLE_NORMAL; 35 | int theme = android.R.style.Theme_Holo_Light_Dialog; 36 | setStyle(style, theme); 37 | } 38 | 39 | @Nullable 40 | @Override 41 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { 42 | View view = inflater.inflate(R.layout.dialog_new_note, container, false); 43 | mTitle = view.findViewById(R.id.note_title); 44 | mContent = view.findViewById(R.id.note_content); 45 | mCreate = view.findViewById(R.id.create); 46 | mCancel = view.findViewById(R.id.cancel); 47 | 48 | mCancel.setOnClickListener(this); 49 | mCreate.setOnClickListener(this); 50 | 51 | getDialog().setTitle("New Note"); 52 | 53 | return view; 54 | } 55 | 56 | @Override 57 | public void onClick(View view) { 58 | switch (view.getId()){ 59 | 60 | case R.id.create:{ 61 | 62 | // insert the new note 63 | 64 | String title = mTitle.getText().toString(); 65 | String content = mContent.getText().toString(); 66 | 67 | if(!title.equals("")){ 68 | mIMainActivity.createNewNote(title, content); 69 | getDialog().dismiss(); 70 | } 71 | else{ 72 | Toast.makeText(getActivity(), "Enter a title", Toast.LENGTH_SHORT).show(); 73 | } 74 | break; 75 | } 76 | 77 | case R.id.cancel:{ 78 | getDialog().dismiss(); 79 | break; 80 | } 81 | } 82 | } 83 | 84 | @Override 85 | public void onAttach(Context context) { 86 | super.onAttach(context); 87 | mIMainActivity = (IMainActivity)getActivity(); 88 | } 89 | } 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /app/src/main/java/codingwithmitch/com/firestoregettingstarted/NoteRecyclerViewAdapter.java: -------------------------------------------------------------------------------- 1 | package codingwithmitch.com.firestoregettingstarted; 2 | 3 | import android.content.Context; 4 | import android.support.annotation.NonNull; 5 | import android.support.v7.widget.RecyclerView; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.widget.ProgressBar; 10 | import android.widget.TextView; 11 | 12 | import java.text.SimpleDateFormat; 13 | import java.util.ArrayList; 14 | 15 | import codingwithmitch.com.firestoregettingstarted.models.Note; 16 | 17 | /** 18 | * Created by User on 5/14/2018. 19 | */ 20 | 21 | public class NoteRecyclerViewAdapter extends RecyclerView.Adapter { 22 | 23 | private static final String TAG = "NoteRecyclerViewAdapter"; 24 | 25 | private ArrayList mNotes = new ArrayList<>(); 26 | private IMainActivity mIMainActivity; 27 | private Context mContext; 28 | private int mSelectedNoteIndex; 29 | 30 | public NoteRecyclerViewAdapter(Context context, ArrayList notes) { 31 | mNotes = notes; 32 | mContext = context; 33 | } 34 | 35 | @NonNull 36 | @Override 37 | public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 38 | RecyclerView.ViewHolder holder; 39 | View view = LayoutInflater.from(parent.getContext()).inflate( 40 | R.layout.layout_note_list_item, parent, false); 41 | 42 | holder = new ViewHolder(view); 43 | 44 | return holder; 45 | } 46 | 47 | @Override 48 | public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { 49 | 50 | if(holder instanceof ViewHolder){ 51 | ((ViewHolder)holder).title.setText(mNotes.get(position).getTitle()); 52 | 53 | SimpleDateFormat spf = new SimpleDateFormat("MMM dd, yyyy"); 54 | String date = spf.format(mNotes.get(position).getTimestamp()); 55 | ((ViewHolder)holder).timestamp.setText(date); 56 | } 57 | } 58 | 59 | @Override 60 | public int getItemCount() { 61 | return mNotes.size(); 62 | } 63 | 64 | 65 | public void updateNote(Note note){ 66 | mNotes.get(mSelectedNoteIndex).setTitle(note.getTitle()); 67 | mNotes.get(mSelectedNoteIndex).setContent(note.getContent()); 68 | notifyDataSetChanged(); 69 | } 70 | 71 | public void removeNote(Note note){ 72 | mNotes.remove(note); 73 | notifyDataSetChanged(); 74 | } 75 | 76 | @Override 77 | public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) { 78 | super.onAttachedToRecyclerView(recyclerView); 79 | mIMainActivity = (IMainActivity) mContext; 80 | } 81 | 82 | public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{ 83 | 84 | TextView title, timestamp; 85 | 86 | public ViewHolder(View itemView) { 87 | super(itemView); 88 | title = itemView.findViewById(R.id.title); 89 | timestamp = itemView.findViewById(R.id.timestamp); 90 | 91 | itemView.setOnClickListener(this); 92 | } 93 | 94 | @Override 95 | public void onClick(View view) { 96 | mSelectedNoteIndex = getAdapterPosition(); 97 | mIMainActivity.onNoteSelected(mNotes.get(mSelectedNoteIndex)); 98 | } 99 | } 100 | } 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /app/src/main/java/codingwithmitch/com/firestoregettingstarted/ViewNoteDialog.java: -------------------------------------------------------------------------------- 1 | package codingwithmitch.com.firestoregettingstarted; 2 | 3 | import android.content.Context; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.support.v4.app.DialogFragment; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.EditText; 11 | import android.widget.TextView; 12 | import android.widget.Toast; 13 | 14 | import codingwithmitch.com.firestoregettingstarted.models.Note; 15 | 16 | /** 17 | * Created by User on 5/14/2018. 18 | */ 19 | 20 | public class ViewNoteDialog extends DialogFragment implements View.OnClickListener{ 21 | 22 | private static final String TAG = "ViewNoteDialog"; 23 | 24 | //widgets 25 | private EditText mTitle, mContent; 26 | private TextView mSave, mDelete; 27 | 28 | //vars 29 | private IMainActivity mIMainActivity; 30 | private Note mNote; 31 | 32 | public static ViewNoteDialog newInstance(Note note) { 33 | ViewNoteDialog dialog = new ViewNoteDialog(); 34 | 35 | Bundle args = new Bundle(); 36 | args.putParcelable("note", note); 37 | dialog.setArguments(args); 38 | 39 | return dialog; 40 | } 41 | 42 | @Override 43 | public void onCreate(@Nullable Bundle savedInstanceState) { 44 | super.onCreate(savedInstanceState); 45 | 46 | int style = DialogFragment.STYLE_NORMAL; 47 | int theme = android.R.style.Theme_Holo_Light_Dialog; 48 | setStyle(style, theme); 49 | 50 | mNote = getArguments().getParcelable("note"); 51 | } 52 | 53 | @Nullable 54 | @Override 55 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) { 56 | View view = inflater.inflate(R.layout.dialog_view_note, container, false); 57 | mTitle = view.findViewById(R.id.note_title); 58 | mContent = view.findViewById(R.id.note_content); 59 | mSave = view.findViewById(R.id.save); 60 | mDelete = view.findViewById(R.id.delete); 61 | 62 | mSave.setOnClickListener(this); 63 | mDelete.setOnClickListener(this); 64 | 65 | getDialog().setTitle("Edit Note"); 66 | 67 | setInitialProperties(); 68 | 69 | return view; 70 | } 71 | 72 | private void setInitialProperties(){ 73 | mTitle.setText(mNote.getTitle()); 74 | mContent.setText(mNote.getContent()); 75 | } 76 | 77 | @Override 78 | public void onClick(View view) { 79 | switch (view.getId()){ 80 | 81 | case R.id.save:{ 82 | 83 | String title = mTitle.getText().toString(); 84 | String content = mContent.getText().toString(); 85 | 86 | if(!title.equals("")){ 87 | 88 | mNote.setTitle(title); 89 | mNote.setContent(content); 90 | 91 | mIMainActivity.updateNote(mNote); 92 | getDialog().dismiss(); 93 | } 94 | else{ 95 | Toast.makeText(getActivity(), "Enter a title", Toast.LENGTH_SHORT).show(); 96 | } 97 | break; 98 | } 99 | 100 | case R.id.delete:{ 101 | mIMainActivity.deleteNote(mNote); 102 | getDialog().dismiss(); 103 | break; 104 | } 105 | } 106 | } 107 | 108 | @Override 109 | public void onAttach(Context context) { 110 | super.onAttach(context); 111 | mIMainActivity = (IMainActivity)getActivity(); 112 | } 113 | } 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /app/src/main/java/codingwithmitch/com/firestoregettingstarted/models/Note.java: -------------------------------------------------------------------------------- 1 | package codingwithmitch.com.firestoregettingstarted.models; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | import com.google.firebase.firestore.IgnoreExtraProperties; 7 | import com.google.firebase.firestore.ServerTimestamp; 8 | 9 | import java.util.Date; 10 | 11 | /** 12 | * Created by User on 5/14/2018. 13 | */ 14 | 15 | @IgnoreExtraProperties 16 | public class Note implements Parcelable{ 17 | 18 | private String title; 19 | private String content; 20 | private @ServerTimestamp Date timestamp; 21 | private String note_id; 22 | private String user_id; 23 | 24 | public Note(String title, String content, Date timestamp, String note_id, String user_id) { 25 | this.title = title; 26 | this.content = content; 27 | this.timestamp = timestamp; 28 | this.note_id = note_id; 29 | this.user_id = user_id; 30 | } 31 | 32 | public Note() { 33 | 34 | } 35 | 36 | protected Note(Parcel in) { 37 | title = in.readString(); 38 | content = in.readString(); 39 | note_id = in.readString(); 40 | user_id = in.readString(); 41 | } 42 | 43 | public static final Creator CREATOR = new Creator() { 44 | @Override 45 | public Note createFromParcel(Parcel in) { 46 | return new Note(in); 47 | } 48 | 49 | @Override 50 | public Note[] newArray(int size) { 51 | return new Note[size]; 52 | } 53 | }; 54 | 55 | public String getUser_id() { 56 | return user_id; 57 | } 58 | 59 | public void setUser_id(String user_id) { 60 | this.user_id = user_id; 61 | } 62 | 63 | public String getTitle() { 64 | return title; 65 | } 66 | 67 | public void setTitle(String title) { 68 | this.title = title; 69 | } 70 | 71 | public String getContent() { 72 | return content; 73 | } 74 | 75 | public void setContent(String content) { 76 | this.content = content; 77 | } 78 | 79 | public Date getTimestamp() { 80 | return timestamp; 81 | } 82 | 83 | public void setTimestamp(Date timestamp) { 84 | this.timestamp = timestamp; 85 | } 86 | 87 | public String getNote_id() { 88 | return note_id; 89 | } 90 | 91 | public void setNote_id(String note_id) { 92 | this.note_id = note_id; 93 | } 94 | 95 | @Override 96 | public int describeContents() { 97 | return 0; 98 | } 99 | 100 | @Override 101 | public void writeToParcel(Parcel parcel, int i) { 102 | parcel.writeString(title); 103 | parcel.writeString(content); 104 | parcel.writeString(note_id); 105 | parcel.writeString(user_id); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_new_note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/FirestoreGettingStarted/af32c7b8ccf6502cc9c530c6b3c006076060ad2e/app/src/main/res/drawable-hdpi/ic_new_note.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_plus_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/FirestoreGettingStarted/af32c7b8ccf6502cc9c530c6b3c006076060ad2e/app/src/main/res/drawable-hdpi/ic_plus_white.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_new_note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/FirestoreGettingStarted/af32c7b8ccf6502cc9c530c6b3c006076060ad2e/app/src/main/res/drawable-mdpi/ic_new_note.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_plus_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/FirestoreGettingStarted/af32c7b8ccf6502cc9c530c6b3c006076060ad2e/app/src/main/res/drawable-mdpi/ic_plus_white.png -------------------------------------------------------------------------------- /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-xhdpi/ic_new_note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/FirestoreGettingStarted/af32c7b8ccf6502cc9c530c6b3c006076060ad2e/app/src/main/res/drawable-xhdpi/ic_new_note.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_plus_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/FirestoreGettingStarted/af32c7b8ccf6502cc9c530c6b3c006076060ad2e/app/src/main/res/drawable-xhdpi/ic_plus_white.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_new_note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/FirestoreGettingStarted/af32c7b8ccf6502cc9c530c6b3c006076060ad2e/app/src/main/res/drawable-xxhdpi/ic_new_note.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_plus_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/FirestoreGettingStarted/af32c7b8ccf6502cc9c530c6b3c006076060ad2e/app/src/main/res/drawable-xxhdpi/ic_plus_white.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/firestore.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mitchtabian/FirestoreGettingStarted/af32c7b8ccf6502cc9c530c6b3c006076060ad2e/app/src/main/res/drawable/firestore.jpeg -------------------------------------------------------------------------------- /app/src/main/res/drawable/grey_border.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | 12 | 14 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 11 | 16 | 21 | 26 | 31 | 36 | 41 | 46 | 51 | 56 | 61 | 66 | 71 | 76 | 81 | 86 | 91 | 96 | 101 | 106 | 111 | 116 | 121 | 126 | 131 | 136 | 141 | 146 | 151 | 156 | 161 | 166 | 171 | 172 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_login.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 11 | 12 | 19 | 20 | 21 | 27 | 28 | 36 | 37 | 38 | 39 | 48 | 49 | 50 |