diff --git a/database/app/build.gradle b/database/app/build.gradle index 8018c3482f..778f1cfc2e 100644 --- a/database/app/build.gradle +++ b/database/app/build.gradle @@ -1,4 +1,8 @@ apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' +apply plugin: 'org.jetbrains.kotlin.android.extensions' + check.dependsOn 'assembleDebugAndroidTest' android { @@ -26,7 +30,15 @@ configurations.all { resolutionStrategy.force 'com.android.support:support-annotations:27.1.1' } +androidExtensions { + experimental = true +} + dependencies { + implementation project(":internal:lintchecks") + implementation project(":internal:chooser") + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.61" + implementation 'com.android.support:appcompat-v7:27.1.1' implementation 'com.android.support:recyclerview-v7:27.1.1' implementation 'com.android.support:cardview-v7:27.1.1' diff --git a/database/app/proguard-rules.pro b/database/app/proguard-rules.pro index ca062aa993..8ff48d15fb 100644 --- a/database/app/proguard-rules.pro +++ b/database/app/proguard-rules.pro @@ -21,10 +21,10 @@ -keepattributes EnclosingMethod -keepattributes InnerClasses --keep class com.google.firebase.quickstart.database.viewholder.** { +-keep class com.google.firebase.quickstart.database.java.viewholder.** { *; } --keepclassmembers class com.google.firebase.quickstart.database.models.** { +-keepclassmembers class com.google.firebase.quickstart.database.java.models.** { *; } diff --git a/database/app/src/androidTest/java/com/google/firebase/quickstart/database/NewPostTest.java b/database/app/src/androidTest/java/com/google/firebase/quickstart/database/NewPostTest.java index 4a25c0a916..a183b04054 100644 --- a/database/app/src/androidTest/java/com/google/firebase/quickstart/database/NewPostTest.java +++ b/database/app/src/androidTest/java/com/google/firebase/quickstart/database/NewPostTest.java @@ -8,6 +8,8 @@ import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.LargeTest; +import com.google.firebase.quickstart.database.java.SignInActivity; + import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -46,50 +48,50 @@ public void newPostTest() { // Select email field ViewInteraction appCompatEditText = onView( - allOf(withId(R.id.field_email), - withParent(withId(R.id.layout_email_password)), + allOf(withId(R.id.fieldEmail), + withParent(withId(R.id.layoutEmailPassword)), isDisplayed())); appCompatEditText.perform(click()); // Enter email address ViewInteraction appCompatEditText2 = onView( - allOf(withId(R.id.field_email), - withParent(withId(R.id.layout_email_password)), + allOf(withId(R.id.fieldEmail), + withParent(withId(R.id.layoutEmailPassword)), isDisplayed())); appCompatEditText2.perform(replaceText(email)); // Enter password ViewInteraction appCompatEditText3 = onView( - allOf(withId(R.id.field_password), - withParent(withId(R.id.layout_email_password)), + allOf(withId(R.id.fieldPassword), + withParent(withId(R.id.layoutEmailPassword)), isDisplayed())); appCompatEditText3.perform(replaceText(password)); // Click sign up ViewInteraction appCompatButton = onView( - allOf(withId(R.id.button_sign_up), withText(R.string.sign_up), - withParent(withId(R.id.layout_buttons)), + allOf(withId(R.id.buttonSignUp), withText(R.string.sign_up), + withParent(withId(R.id.layoutButtons)), isDisplayed())); appCompatButton.perform(click()); // Click new post button ViewInteraction floatingActionButton = onView( - allOf(withId(R.id.fab_new_post), isDisplayed())); + allOf(withId(R.id.fabNewPost), isDisplayed())); floatingActionButton.perform(click()); // Enter post title ViewInteraction appCompatEditText4 = onView( - allOf(withId(R.id.field_title), isDisplayed())); + allOf(withId(R.id.fieldTitle), isDisplayed())); appCompatEditText4.perform(replaceText(postTitle)); // Enter post content ViewInteraction appCompatEditText5 = onView( - allOf(withId(R.id.field_body), isDisplayed())); + allOf(withId(R.id.fieldBody), isDisplayed())); appCompatEditText5.perform(replaceText(postContent)); // Click submit button ViewInteraction floatingActionButton2 = onView( - allOf(withId(R.id.fab_submit_post), isDisplayed())); + allOf(withId(R.id.fabSubmitPost), isDisplayed())); floatingActionButton2.perform(click()); // Navigate to "My Posts" @@ -99,18 +101,18 @@ public void newPostTest() { // Check that the title is correct ViewInteraction textView = onView( - allOf(withId(R.id.post_title), withText(postTitle), isDisplayed())); + allOf(withId(R.id.postTitle), withText(postTitle), isDisplayed())); textView.check(matches(withText(postTitle))); // Check that the content is correct ViewInteraction textView2 = onView( - allOf(withId(R.id.post_body), withText(postContent), isDisplayed())); + allOf(withId(R.id.postBody), withText(postContent), isDisplayed())); textView2.check(matches(withText(postContent))); // Check that it has zero stars ViewInteraction textView3 = onView( - allOf(withId(R.id.post_num_stars), withText("0"), - withParent(withId(R.id.star_layout)), + allOf(withId(R.id.postNumStars), withText("0"), + withParent(withId(R.id.starLayout)), isDisplayed())); textView3.check(matches(withText("0"))); diff --git a/database/app/src/main/AndroidManifest.xml b/database/app/src/main/AndroidManifest.xml index 31650c4463..9e54173bb2 100644 --- a/database/app/src/main/AndroidManifest.xml +++ b/database/app/src/main/AndroidManifest.xml @@ -8,19 +8,33 @@ android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> - - - + + - + + + + + + + + + + + + + diff --git a/database/app/src/main/java/com/google/firebase/quickstart/database/EntryChoiceActivity.kt b/database/app/src/main/java/com/google/firebase/quickstart/database/EntryChoiceActivity.kt new file mode 100644 index 0000000000..ca165d47f0 --- /dev/null +++ b/database/app/src/main/java/com/google/firebase/quickstart/database/EntryChoiceActivity.kt @@ -0,0 +1,22 @@ +package com.google.firebase.quickstart.database + +import android.content.Intent +import com.firebase.example.internal.BaseEntryChoiceActivity +import com.firebase.example.internal.Choice + +class EntryChoiceActivity : BaseEntryChoiceActivity() { + + override fun getChoices(): List { + return listOf( + Choice( + "Java", + "Run the Firebase Realtime Database quickstart written in Java.", + Intent(this, com.google.firebase.quickstart.database.java.MainActivity::class.java)), + Choice( + "Kotlin", + "Run the Firebase Realtime Database quickstart written in Kotlin.", + Intent(this, com.google.firebase.quickstart.database.kotlin.MainActivity::class.java)) + ) + } + +} diff --git a/database/app/src/main/java/com/google/firebase/quickstart/database/BaseActivity.java b/database/app/src/main/java/com/google/firebase/quickstart/database/java/BaseActivity.java similarity index 93% rename from database/app/src/main/java/com/google/firebase/quickstart/database/BaseActivity.java rename to database/app/src/main/java/com/google/firebase/quickstart/database/java/BaseActivity.java index 481246eddd..c2e7306da9 100644 --- a/database/app/src/main/java/com/google/firebase/quickstart/database/BaseActivity.java +++ b/database/app/src/main/java/com/google/firebase/quickstart/database/java/BaseActivity.java @@ -1,4 +1,4 @@ -package com.google.firebase.quickstart.database; +package com.google.firebase.quickstart.database.java; import android.app.ProgressDialog; import android.support.v7.app.AppCompatActivity; diff --git a/database/app/src/main/java/com/google/firebase/quickstart/database/MainActivity.java b/database/app/src/main/java/com/google/firebase/quickstart/database/java/MainActivity.java similarity index 88% rename from database/app/src/main/java/com/google/firebase/quickstart/database/MainActivity.java rename to database/app/src/main/java/com/google/firebase/quickstart/database/java/MainActivity.java index bc11df00a2..b738a6ad30 100644 --- a/database/app/src/main/java/com/google/firebase/quickstart/database/MainActivity.java +++ b/database/app/src/main/java/com/google/firebase/quickstart/database/java/MainActivity.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.google.firebase.quickstart.database; +package com.google.firebase.quickstart.database.java; import android.content.Intent; import android.os.Bundle; @@ -27,9 +27,10 @@ import android.view.View; import com.google.firebase.auth.FirebaseAuth; -import com.google.firebase.quickstart.database.fragment.MyPostsFragment; -import com.google.firebase.quickstart.database.fragment.MyTopPostsFragment; -import com.google.firebase.quickstart.database.fragment.RecentPostsFragment; +import com.google.firebase.quickstart.database.R; +import com.google.firebase.quickstart.database.java.fragment.MyPostsFragment; +import com.google.firebase.quickstart.database.java.fragment.MyTopPostsFragment; +import com.google.firebase.quickstart.database.java.fragment.RecentPostsFragment; public class MainActivity extends BaseActivity { @@ -75,7 +76,7 @@ public CharSequence getPageTitle(int position) { tabLayout.setupWithViewPager(mViewPager); // Button launches NewPostActivity - findViewById(R.id.fab_new_post).setOnClickListener(new View.OnClickListener() { + findViewById(R.id.fabNewPost).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(MainActivity.this, NewPostActivity.class)); diff --git a/database/app/src/main/java/com/google/firebase/quickstart/database/NewPostActivity.java b/database/app/src/main/java/com/google/firebase/quickstart/database/java/NewPostActivity.java similarity index 92% rename from database/app/src/main/java/com/google/firebase/quickstart/database/NewPostActivity.java rename to database/app/src/main/java/com/google/firebase/quickstart/database/java/NewPostActivity.java index a2a378deae..ad11b716e0 100644 --- a/database/app/src/main/java/com/google/firebase/quickstart/database/NewPostActivity.java +++ b/database/app/src/main/java/com/google/firebase/quickstart/database/java/NewPostActivity.java @@ -1,4 +1,4 @@ -package com.google.firebase.quickstart.database; +package com.google.firebase.quickstart.database.java; import android.os.Bundle; import android.support.design.widget.FloatingActionButton; @@ -13,8 +13,9 @@ import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.FirebaseDatabase; import com.google.firebase.database.ValueEventListener; -import com.google.firebase.quickstart.database.models.Post; -import com.google.firebase.quickstart.database.models.User; +import com.google.firebase.quickstart.database.R; +import com.google.firebase.quickstart.database.java.models.Post; +import com.google.firebase.quickstart.database.java.models.User; import java.util.HashMap; import java.util.Map; @@ -41,9 +42,9 @@ protected void onCreate(Bundle savedInstanceState) { mDatabase = FirebaseDatabase.getInstance().getReference(); // [END initialize_database_ref] - mTitleField = findViewById(R.id.field_title); - mBodyField = findViewById(R.id.field_body); - mSubmitButton = findViewById(R.id.fab_submit_post); + mTitleField = findViewById(R.id.fieldTitle); + mBodyField = findViewById(R.id.fieldBody); + mSubmitButton = findViewById(R.id.fabSubmitPost); mSubmitButton.setOnClickListener(new View.OnClickListener() { @Override diff --git a/database/app/src/main/java/com/google/firebase/quickstart/database/PostDetailActivity.java b/database/app/src/main/java/com/google/firebase/quickstart/database/java/PostDetailActivity.java similarity index 93% rename from database/app/src/main/java/com/google/firebase/quickstart/database/PostDetailActivity.java rename to database/app/src/main/java/com/google/firebase/quickstart/database/java/PostDetailActivity.java index 3818e5c43b..d375348c59 100644 --- a/database/app/src/main/java/com/google/firebase/quickstart/database/PostDetailActivity.java +++ b/database/app/src/main/java/com/google/firebase/quickstart/database/java/PostDetailActivity.java @@ -1,4 +1,4 @@ -package com.google.firebase.quickstart.database; +package com.google.firebase.quickstart.database.java; import android.content.Context; import android.os.Bundle; @@ -19,9 +19,10 @@ import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.FirebaseDatabase; import com.google.firebase.database.ValueEventListener; -import com.google.firebase.quickstart.database.models.User; -import com.google.firebase.quickstart.database.models.Comment; -import com.google.firebase.quickstart.database.models.Post; +import com.google.firebase.quickstart.database.R; +import com.google.firebase.quickstart.database.java.models.User; +import com.google.firebase.quickstart.database.java.models.Comment; +import com.google.firebase.quickstart.database.java.models.Post; import java.util.ArrayList; import java.util.List; @@ -63,12 +64,12 @@ protected void onCreate(Bundle savedInstanceState) { .child("post-comments").child(mPostKey); // Initialize Views - mAuthorView = findViewById(R.id.post_author); - mTitleView = findViewById(R.id.post_title); - mBodyView = findViewById(R.id.post_body); - mCommentField = findViewById(R.id.field_comment_text); - mCommentButton = findViewById(R.id.button_post_comment); - mCommentsRecycler = findViewById(R.id.recycler_comments); + mAuthorView = findViewById(R.id.postAuthor); + mTitleView = findViewById(R.id.postTitle); + mBodyView = findViewById(R.id.postBody); + mCommentField = findViewById(R.id.fieldCommentText); + mCommentButton = findViewById(R.id.buttonPostComment); + mCommentsRecycler = findViewById(R.id.recyclerPostComments); mCommentButton.setOnClickListener(this); mCommentsRecycler.setLayoutManager(new LinearLayoutManager(this)); @@ -130,7 +131,7 @@ public void onStop() { @Override public void onClick(View v) { int i = v.getId(); - if (i == R.id.button_post_comment) { + if (i == R.id.buttonPostComment) { postComment(); } } @@ -171,8 +172,8 @@ private static class CommentViewHolder extends RecyclerView.ViewHolder { public CommentViewHolder(View itemView) { super(itemView); - authorView = itemView.findViewById(R.id.comment_author); - bodyView = itemView.findViewById(R.id.comment_body); + authorView = itemView.findViewById(R.id.commentAuthor); + bodyView = itemView.findViewById(R.id.commentBody); } } diff --git a/database/app/src/main/java/com/google/firebase/quickstart/database/SignInActivity.java b/database/app/src/main/java/com/google/firebase/quickstart/database/java/SignInActivity.java similarity index 91% rename from database/app/src/main/java/com/google/firebase/quickstart/database/SignInActivity.java rename to database/app/src/main/java/com/google/firebase/quickstart/database/java/SignInActivity.java index 5aa24cac1f..3b81edfe0a 100644 --- a/database/app/src/main/java/com/google/firebase/quickstart/database/SignInActivity.java +++ b/database/app/src/main/java/com/google/firebase/quickstart/database/java/SignInActivity.java @@ -1,4 +1,4 @@ -package com.google.firebase.quickstart.database; +package com.google.firebase.quickstart.database.java; import android.content.Intent; import android.os.Bundle; @@ -17,7 +17,8 @@ import com.google.firebase.auth.FirebaseUser; import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.FirebaseDatabase; -import com.google.firebase.quickstart.database.models.User; +import com.google.firebase.quickstart.database.R; +import com.google.firebase.quickstart.database.java.models.User; public class SignInActivity extends BaseActivity implements View.OnClickListener { @@ -40,10 +41,10 @@ protected void onCreate(Bundle savedInstanceState) { mAuth = FirebaseAuth.getInstance(); // Views - mEmailField = findViewById(R.id.field_email); - mPasswordField = findViewById(R.id.field_password); - mSignInButton = findViewById(R.id.button_sign_in); - mSignUpButton = findViewById(R.id.button_sign_up); + mEmailField = findViewById(R.id.fieldEmail); + mPasswordField = findViewById(R.id.fieldPassword); + mSignInButton = findViewById(R.id.buttonSignIn); + mSignUpButton = findViewById(R.id.buttonSignUp); // Click listeners mSignInButton.setOnClickListener(this); @@ -163,9 +164,9 @@ private void writeNewUser(String userId, String name, String email) { @Override public void onClick(View v) { int i = v.getId(); - if (i == R.id.button_sign_in) { + if (i == R.id.buttonSignIn) { signIn(); - } else if (i == R.id.button_sign_up) { + } else if (i == R.id.buttonSignUp) { signUp(); } } diff --git a/database/app/src/main/java/com/google/firebase/quickstart/database/fragment/MyPostsFragment.java b/database/app/src/main/java/com/google/firebase/quickstart/database/java/fragment/MyPostsFragment.java similarity index 86% rename from database/app/src/main/java/com/google/firebase/quickstart/database/fragment/MyPostsFragment.java rename to database/app/src/main/java/com/google/firebase/quickstart/database/java/fragment/MyPostsFragment.java index 79e08561ba..fbec06163a 100644 --- a/database/app/src/main/java/com/google/firebase/quickstart/database/fragment/MyPostsFragment.java +++ b/database/app/src/main/java/com/google/firebase/quickstart/database/java/fragment/MyPostsFragment.java @@ -1,4 +1,4 @@ -package com.google.firebase.quickstart.database.fragment; +package com.google.firebase.quickstart.database.java.fragment; import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.Query; diff --git a/database/app/src/main/java/com/google/firebase/quickstart/database/fragment/MyTopPostsFragment.java b/database/app/src/main/java/com/google/firebase/quickstart/database/java/fragment/MyTopPostsFragment.java similarity index 90% rename from database/app/src/main/java/com/google/firebase/quickstart/database/fragment/MyTopPostsFragment.java rename to database/app/src/main/java/com/google/firebase/quickstart/database/java/fragment/MyTopPostsFragment.java index ed197abade..8600d1d32e 100644 --- a/database/app/src/main/java/com/google/firebase/quickstart/database/fragment/MyTopPostsFragment.java +++ b/database/app/src/main/java/com/google/firebase/quickstart/database/java/fragment/MyTopPostsFragment.java @@ -1,4 +1,4 @@ -package com.google.firebase.quickstart.database.fragment; +package com.google.firebase.quickstart.database.java.fragment; import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.Query; diff --git a/database/app/src/main/java/com/google/firebase/quickstart/database/fragment/PostListFragment.java b/database/app/src/main/java/com/google/firebase/quickstart/database/java/fragment/PostListFragment.java similarity index 95% rename from database/app/src/main/java/com/google/firebase/quickstart/database/fragment/PostListFragment.java rename to database/app/src/main/java/com/google/firebase/quickstart/database/java/fragment/PostListFragment.java index f2ec666cc7..bde0fc09be 100644 --- a/database/app/src/main/java/com/google/firebase/quickstart/database/fragment/PostListFragment.java +++ b/database/app/src/main/java/com/google/firebase/quickstart/database/java/fragment/PostListFragment.java @@ -1,4 +1,4 @@ -package com.google.firebase.quickstart.database.fragment; +package com.google.firebase.quickstart.database.java.fragment; import android.content.Intent; import android.os.Bundle; @@ -20,10 +20,10 @@ import com.google.firebase.database.MutableData; import com.google.firebase.database.Query; import com.google.firebase.database.Transaction; -import com.google.firebase.quickstart.database.PostDetailActivity; +import com.google.firebase.quickstart.database.java.PostDetailActivity; import com.google.firebase.quickstart.database.R; -import com.google.firebase.quickstart.database.models.Post; -import com.google.firebase.quickstart.database.viewholder.PostViewHolder; +import com.google.firebase.quickstart.database.java.models.Post; +import com.google.firebase.quickstart.database.java.viewholder.PostViewHolder; public abstract class PostListFragment extends Fragment { @@ -49,7 +49,7 @@ public View onCreateView (LayoutInflater inflater, ViewGroup container, mDatabase = FirebaseDatabase.getInstance().getReference(); // [END create_database_reference] - mRecycler = rootView.findViewById(R.id.messages_list); + mRecycler = rootView.findViewById(R.id.messagesList); mRecycler.setHasFixedSize(true); return rootView; diff --git a/database/app/src/main/java/com/google/firebase/quickstart/database/fragment/RecentPostsFragment.java b/database/app/src/main/java/com/google/firebase/quickstart/database/java/fragment/RecentPostsFragment.java similarity index 90% rename from database/app/src/main/java/com/google/firebase/quickstart/database/fragment/RecentPostsFragment.java rename to database/app/src/main/java/com/google/firebase/quickstart/database/java/fragment/RecentPostsFragment.java index 8038d5713f..48c936d307 100644 --- a/database/app/src/main/java/com/google/firebase/quickstart/database/fragment/RecentPostsFragment.java +++ b/database/app/src/main/java/com/google/firebase/quickstart/database/java/fragment/RecentPostsFragment.java @@ -1,4 +1,4 @@ -package com.google.firebase.quickstart.database.fragment; +package com.google.firebase.quickstart.database.java.fragment; import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.Query; diff --git a/database/app/src/main/java/com/google/firebase/quickstart/database/models/Comment.java b/database/app/src/main/java/com/google/firebase/quickstart/database/java/models/Comment.java similarity index 89% rename from database/app/src/main/java/com/google/firebase/quickstart/database/models/Comment.java rename to database/app/src/main/java/com/google/firebase/quickstart/database/java/models/Comment.java index 7e734941ae..f670f3900c 100644 --- a/database/app/src/main/java/com/google/firebase/quickstart/database/models/Comment.java +++ b/database/app/src/main/java/com/google/firebase/quickstart/database/java/models/Comment.java @@ -1,4 +1,4 @@ -package com.google.firebase.quickstart.database.models; +package com.google.firebase.quickstart.database.java.models; import com.google.firebase.database.IgnoreExtraProperties; diff --git a/database/app/src/main/java/com/google/firebase/quickstart/database/models/Post.java b/database/app/src/main/java/com/google/firebase/quickstart/database/java/models/Post.java similarity index 94% rename from database/app/src/main/java/com/google/firebase/quickstart/database/models/Post.java rename to database/app/src/main/java/com/google/firebase/quickstart/database/java/models/Post.java index 71dbea00f7..0c42d38fd3 100644 --- a/database/app/src/main/java/com/google/firebase/quickstart/database/models/Post.java +++ b/database/app/src/main/java/com/google/firebase/quickstart/database/java/models/Post.java @@ -1,4 +1,4 @@ -package com.google.firebase.quickstart.database.models; +package com.google.firebase.quickstart.database.java.models; import com.google.firebase.database.Exclude; import com.google.firebase.database.IgnoreExtraProperties; diff --git a/database/app/src/main/java/com/google/firebase/quickstart/database/models/User.java b/database/app/src/main/java/com/google/firebase/quickstart/database/java/models/User.java similarity index 87% rename from database/app/src/main/java/com/google/firebase/quickstart/database/models/User.java rename to database/app/src/main/java/com/google/firebase/quickstart/database/java/models/User.java index 595b7099a4..d8c241dce4 100644 --- a/database/app/src/main/java/com/google/firebase/quickstart/database/models/User.java +++ b/database/app/src/main/java/com/google/firebase/quickstart/database/java/models/User.java @@ -1,4 +1,4 @@ -package com.google.firebase.quickstart.database.models; +package com.google.firebase.quickstart.database.java.models; import com.google.firebase.database.IgnoreExtraProperties; diff --git a/database/app/src/main/java/com/google/firebase/quickstart/database/viewholder/PostViewHolder.java b/database/app/src/main/java/com/google/firebase/quickstart/database/java/viewholder/PostViewHolder.java similarity index 69% rename from database/app/src/main/java/com/google/firebase/quickstart/database/viewholder/PostViewHolder.java rename to database/app/src/main/java/com/google/firebase/quickstart/database/java/viewholder/PostViewHolder.java index fbf26abac0..f3e1609ad6 100644 --- a/database/app/src/main/java/com/google/firebase/quickstart/database/viewholder/PostViewHolder.java +++ b/database/app/src/main/java/com/google/firebase/quickstart/database/java/viewholder/PostViewHolder.java @@ -1,4 +1,4 @@ -package com.google.firebase.quickstart.database.viewholder; +package com.google.firebase.quickstart.database.java.viewholder; import android.support.v7.widget.RecyclerView; import android.view.View; @@ -6,7 +6,7 @@ import android.widget.TextView; import com.google.firebase.quickstart.database.R; -import com.google.firebase.quickstart.database.models.Post; +import com.google.firebase.quickstart.database.java.models.Post; public class PostViewHolder extends RecyclerView.ViewHolder { @@ -19,11 +19,11 @@ public class PostViewHolder extends RecyclerView.ViewHolder { public PostViewHolder(View itemView) { super(itemView); - titleView = itemView.findViewById(R.id.post_title); - authorView = itemView.findViewById(R.id.post_author); + titleView = itemView.findViewById(R.id.postTitle); + authorView = itemView.findViewById(R.id.postAuthor); starView = itemView.findViewById(R.id.star); - numStarsView = itemView.findViewById(R.id.post_num_stars); - bodyView = itemView.findViewById(R.id.post_body); + numStarsView = itemView.findViewById(R.id.postNumStars); + bodyView = itemView.findViewById(R.id.postBody); } public void bindToPost(Post post, View.OnClickListener starClickListener) { diff --git a/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/BaseActivity.kt b/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/BaseActivity.kt new file mode 100644 index 0000000000..5668de7575 --- /dev/null +++ b/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/BaseActivity.kt @@ -0,0 +1,35 @@ +package com.google.firebase.quickstart.database.kotlin + +import android.app.ProgressDialog +import android.support.v7.app.AppCompatActivity +import com.google.firebase.auth.FirebaseAuth + +open class BaseActivity : AppCompatActivity() { + + private var progressDialog: ProgressDialog? = null + + val uid: String + get() = FirebaseAuth.getInstance().currentUser!!.uid + + fun showProgressDialog() { + if (progressDialog == null) { + val pd = ProgressDialog(this) + pd.setCancelable(false) + pd.setMessage("Loading...") + + progressDialog = pd + } + + progressDialog?.show() + } + + fun hideProgressDialog() { + progressDialog?.let { + if (it.isShowing) { + it.dismiss() + } + } + } + + +} diff --git a/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/MainActivity.kt b/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/MainActivity.kt new file mode 100644 index 0000000000..f9505d87e1 --- /dev/null +++ b/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/MainActivity.kt @@ -0,0 +1,80 @@ +package com.google.firebase.quickstart.database.kotlin + +import android.content.Intent +import android.os.Bundle +import android.support.v4.app.Fragment +import android.support.v4.app.FragmentPagerAdapter +import android.view.Menu +import android.view.MenuItem +import com.google.firebase.auth.FirebaseAuth +import com.google.firebase.quickstart.database.R +import com.google.firebase.quickstart.database.kotlin.fragment.MyPostsFragment +import com.google.firebase.quickstart.database.kotlin.fragment.MyTopPostsFragment +import com.google.firebase.quickstart.database.kotlin.fragment.RecentPostsFragment +import kotlinx.android.synthetic.main.activity_main.* + +class MainActivity : BaseActivity() { + + private lateinit var pagerAdapter: FragmentPagerAdapter + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + + // Create the adapter that will return a fragment for each section + pagerAdapter = object : FragmentPagerAdapter(supportFragmentManager) { + private val fragments = arrayOf( + RecentPostsFragment(), + MyPostsFragment(), + MyTopPostsFragment()) + + private val fragmentNames = arrayOf( + getString(R.string.heading_recent), + getString(R.string.heading_my_posts), + getString(R.string.heading_my_top_posts)) + + override fun getItem(position: Int): Fragment { + return fragments[position] + } + + override fun getCount() = fragments.size + + override fun getPageTitle(position: Int): CharSequence? { + return fragmentNames[position] + } + } + + // Set up the ViewPager with the sections adapter. + container.adapter = pagerAdapter + tabs.setupWithViewPager(container) + + // Button launches NewPostActivity + fabNewPost.setOnClickListener { + startActivity(Intent(this, NewPostActivity::class.java)) + } + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.menu_main, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + val i = item.itemId + if (i == R.id.action_logout) { + FirebaseAuth.getInstance().signOut() + startActivity(Intent(this, SignInActivity::class.java)) + finish() + return true + } else { + return super.onOptionsItemSelected(item) + } + } + + companion object { + + private const val TAG = "MainActivity" + + } + +} diff --git a/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/NewPostActivity.kt b/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/NewPostActivity.kt new file mode 100644 index 0000000000..720151a56f --- /dev/null +++ b/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/NewPostActivity.kt @@ -0,0 +1,125 @@ +package com.google.firebase.quickstart.database.kotlin + +import android.os.Bundle +import android.text.TextUtils +import android.util.Log +import android.view.View +import android.widget.Toast +import com.google.firebase.database.* +import com.google.firebase.quickstart.database.R +import com.google.firebase.quickstart.database.kotlin.models.Post +import com.google.firebase.quickstart.database.kotlin.models.User +import kotlinx.android.synthetic.main.activity_new_post.* +import java.util.* + +class NewPostActivity : BaseActivity() { + + // [START declare_database_ref] + private lateinit var database: DatabaseReference + // [END declare_database_ref] + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_new_post) + + // [START initialize_database_ref] + database = FirebaseDatabase.getInstance().reference + // [END initialize_database_ref] + + fabSubmitPost.setOnClickListener { submitPost() } + } + + private fun submitPost() { + val title = fieldTitle.text.toString() + val body = fieldBody.text.toString() + + // Title is required + if (TextUtils.isEmpty(title)) { + fieldTitle.error = REQUIRED + return + } + + // Body is required + if (TextUtils.isEmpty(body)) { + fieldBody.error = REQUIRED + return + } + + // Disable button so there are no multi-posts + setEditingEnabled(false) + Toast.makeText(this, "Posting...", Toast.LENGTH_SHORT).show() + + // [START single_value_read] + val userId = uid + database.child("users").child(userId).addListenerForSingleValueEvent( + object : ValueEventListener { + override fun onDataChange(dataSnapshot: DataSnapshot) { + // Get user value + val user = dataSnapshot.getValue(User::class.java) + + // [START_EXCLUDE] + if (user == null) { + // User is null, error out + Log.e(TAG, "User $userId is unexpectedly null") + Toast.makeText(baseContext, + "Error: could not fetch user.", + Toast.LENGTH_SHORT).show() + } else { + // Write new post + writeNewPost(userId, user.username.toString(), title, body) + } + + // Finish this Activity, back to the stream + setEditingEnabled(true) + finish() + // [END_EXCLUDE] + } + + override fun onCancelled(databaseError: DatabaseError) { + Log.w(TAG, "getUser:onCancelled", databaseError.toException()) + // [START_EXCLUDE] + setEditingEnabled(true) + // [END_EXCLUDE] + } + }) + // [END single_value_read] + } + + private fun setEditingEnabled(enabled: Boolean) { + fieldTitle.isEnabled = enabled + fieldBody.isEnabled = enabled + fabSubmitPost.visibility = if (enabled) { + View.VISIBLE + } else { + View.GONE + } + } + + // [START write_fan_out] + private fun writeNewPost(userId: String, username: String, title: String, body: String) { + // Create new post at /user-posts/$userid/$postid and at + // /posts/$postid simultaneously + val key = database.child("posts").push().key + if (key == null) { + Log.w(TAG, "Couldn't get push key for posts") + return + } + + val post = Post(userId, username, title, body) + val postValues = post.toMap() + + val childUpdates = HashMap() + childUpdates["/posts/$key"] = postValues + childUpdates["/user-posts/$userId/$key"] = postValues + + database.updateChildren(childUpdates) + } + // [END write_fan_out] + + companion object { + + private const val TAG = "NewPostActivity" + private const val REQUIRED = "Required" + + } +} diff --git a/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/PostDetailActivity.kt b/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/PostDetailActivity.kt new file mode 100644 index 0000000000..ba3962ab86 --- /dev/null +++ b/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/PostDetailActivity.kt @@ -0,0 +1,269 @@ +package com.google.firebase.quickstart.database.kotlin + +import android.content.Context +import android.os.Bundle +import android.support.v7.widget.LinearLayoutManager +import android.support.v7.widget.RecyclerView +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import com.google.firebase.database.* +import com.google.firebase.quickstart.database.R +import com.google.firebase.quickstart.database.kotlin.models.Comment +import com.google.firebase.quickstart.database.kotlin.models.Post +import com.google.firebase.quickstart.database.kotlin.models.User +import kotlinx.android.synthetic.main.activity_post_detail.* +import kotlinx.android.synthetic.main.include_post_author.* +import kotlinx.android.synthetic.main.include_post_text.* +import kotlinx.android.synthetic.main.item_comment.view.* +import java.util.* + +class PostDetailActivity : BaseActivity(), View.OnClickListener { + + private lateinit var postKey: String + private lateinit var postReference: DatabaseReference + private lateinit var commentsReference: DatabaseReference + + private var postListener: ValueEventListener? = null + private var adapter: CommentAdapter? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_post_detail) + + // Get post key from intent + postKey = intent.getStringExtra(EXTRA_POST_KEY) ?: + throw IllegalArgumentException("Must pass EXTRA_POST_KEY") + + // Initialize Database + postReference = FirebaseDatabase.getInstance().reference + .child("posts").child(postKey) + commentsReference = FirebaseDatabase.getInstance().reference + .child("post-comments").child(postKey) + + // Initialize Views + buttonPostComment.setOnClickListener(this) + recyclerPostComments.layoutManager = LinearLayoutManager(this) + + } + + public override fun onStart() { + super.onStart() + + // Add value event listener to the post + // [START post_value_event_listener] + val postListener = object : ValueEventListener { + override fun onDataChange(dataSnapshot: DataSnapshot) { + // Get Post object and use the values to update the UI + val post = dataSnapshot.getValue(Post::class.java) + // [START_EXCLUDE] + post?.let { + postAuthor.text = it.author + postTitle.text = it.title + postBody.text = it.body + } + // [END_EXCLUDE] + } + + override fun onCancelled(databaseError: DatabaseError) { + // Getting Post failed, log a message + Log.w(TAG, "loadPost:onCancelled", databaseError.toException()) + // [START_EXCLUDE] + Toast.makeText(baseContext, "Failed to load post.", + Toast.LENGTH_SHORT).show() + // [END_EXCLUDE] + } + } + postReference.addValueEventListener(postListener) + // [END post_value_event_listener] + + // Keep copy of post listener so we can remove it when app stops + this.postListener = postListener + + // Listen for comments + adapter = CommentAdapter(this, commentsReference) + recyclerPostComments.adapter = adapter + } + + public override fun onStop() { + super.onStop() + + // Remove post value event listener + postListener?.let { + postReference.removeEventListener(it) + } + + // Clean up comments listener + adapter?.cleanupListener() + } + + override fun onClick(v: View) { + val i = v.id + if (i == R.id.buttonPostComment) { + postComment() + } + } + + private fun postComment() { + val uid = uid + FirebaseDatabase.getInstance().reference.child("users").child(uid) + .addListenerForSingleValueEvent(object : ValueEventListener { + override fun onDataChange(dataSnapshot: DataSnapshot) { + // Get user information + val user = dataSnapshot.getValue(User::class.java) + if (user == null) { + return + } + + val authorName = user.username + + // Create new comment object + val commentText = fieldCommentText.text.toString() + val comment = Comment(uid, authorName, commentText) + + // Push the comment, it will appear in the list + commentsReference.push().setValue(comment) + + // Clear the field + fieldCommentText.text = null + } + + override fun onCancelled(databaseError: DatabaseError) { + + } + }) + } + + private class CommentViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + + fun bind(comment: Comment) { + itemView.commentAuthor.text = comment.author + itemView.commentBody.text = comment.text + } + + } + + private class CommentAdapter(private val context: Context, + private val databaseReference: DatabaseReference) : RecyclerView.Adapter() { + + private val childEventListener: ChildEventListener? + + private val commentIds = ArrayList() + private val comments = ArrayList() + + init { + + // Create child event listener + // [START child_event_listener_recycler] + val childEventListener = object : ChildEventListener { + override fun onChildAdded(dataSnapshot: DataSnapshot, previousChildName: String?) { + Log.d(TAG, "onChildAdded:" + dataSnapshot.key!!) + + // A new comment has been added, add it to the displayed list + val comment = dataSnapshot.getValue(Comment::class.java) + + // [START_EXCLUDE] + // Update RecyclerView + commentIds.add(dataSnapshot.key!!) + comments.add(comment!!) + notifyItemInserted(comments.size - 1) + // [END_EXCLUDE] + } + + override fun onChildChanged(dataSnapshot: DataSnapshot, previousChildName: String?) { + Log.d(TAG, "onChildChanged: ${dataSnapshot.key}") + + // A comment has changed, use the key to determine if we are displaying this + // comment and if so displayed the changed comment. + val newComment = dataSnapshot.getValue(Comment::class.java) + val commentKey = dataSnapshot.key + + // [START_EXCLUDE] + val commentIndex = commentIds.indexOf(commentKey) + if (commentIndex > -1 && newComment != null) { + // Replace with the new data + comments.set(commentIndex, newComment) + + // Update the RecyclerView + notifyItemChanged(commentIndex) + } else { + Log.w(TAG, "onChildChanged:unknown_child: $commentKey") + } + // [END_EXCLUDE] + } + + override fun onChildRemoved(dataSnapshot: DataSnapshot) { + Log.d(TAG, "onChildRemoved:" + dataSnapshot.key!!) + + // A comment has changed, use the key to determine if we are displaying this + // comment and if so remove it. + val commentKey = dataSnapshot.key + + // [START_EXCLUDE] + val commentIndex = commentIds.indexOf(commentKey) + if (commentIndex > -1) { + // Remove data from the list + commentIds.removeAt(commentIndex) + comments.removeAt(commentIndex) + + // Update the RecyclerView + notifyItemRemoved(commentIndex) + } else { + Log.w(TAG, "onChildRemoved:unknown_child:" + commentKey!!) + } + // [END_EXCLUDE] + } + + override fun onChildMoved(dataSnapshot: DataSnapshot, previousChildName: String?) { + Log.d(TAG, "onChildMoved:" + dataSnapshot.key!!) + + // A comment has changed position, use the key to determine if we are + // displaying this comment and if so move it. + val movedComment = dataSnapshot.getValue(Comment::class.java) + val commentKey = dataSnapshot.key + + // ... + } + + override fun onCancelled(databaseError: DatabaseError) { + Log.w(TAG, "postComments:onCancelled", databaseError.toException()) + Toast.makeText(context, "Failed to load comments.", + Toast.LENGTH_SHORT).show() + } + } + databaseReference.addChildEventListener(childEventListener) + // [END child_event_listener_recycler] + + // Store reference to listener so it can be removed on app stop + this.childEventListener = childEventListener + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CommentViewHolder { + val inflater = LayoutInflater.from(context) + val view = inflater.inflate(R.layout.item_comment, parent, false) + return CommentViewHolder(view) + } + + override fun onBindViewHolder(holder: CommentViewHolder, position: Int) { + holder.bind(comments[position]) + } + + override fun getItemCount(): Int = comments.size + + fun cleanupListener() { + childEventListener?.let { + databaseReference.removeEventListener(it) + } + } + + } + + companion object { + + private const val TAG = "PostDetailActivity" + const val EXTRA_POST_KEY = "post_key" + + } +} diff --git a/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/SignInActivity.kt b/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/SignInActivity.kt new file mode 100644 index 0000000000..0480a9fc2a --- /dev/null +++ b/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/SignInActivity.kt @@ -0,0 +1,150 @@ +package com.google.firebase.quickstart.database.kotlin + +import android.content.Intent +import android.os.Bundle +import android.text.TextUtils +import android.util.Log +import android.view.View +import android.widget.Toast +import com.google.firebase.auth.FirebaseAuth +import com.google.firebase.auth.FirebaseUser +import com.google.firebase.database.DatabaseReference +import com.google.firebase.database.FirebaseDatabase +import com.google.firebase.quickstart.database.R +import com.google.firebase.quickstart.database.kotlin.models.User +import kotlinx.android.synthetic.main.activity_sign_in.* + +class SignInActivity : BaseActivity(), View.OnClickListener { + + private lateinit var database: DatabaseReference + private lateinit var auth: FirebaseAuth + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_sign_in) + + database = FirebaseDatabase.getInstance().reference + auth = FirebaseAuth.getInstance() + + // Click listeners + buttonSignIn.setOnClickListener(this) + buttonSignUp.setOnClickListener(this) + } + + public override fun onStart() { + super.onStart() + + // Check auth on Activity start + auth.currentUser?.let { + onAuthSuccess(it) + } + } + + private fun signIn() { + Log.d(TAG, "signIn") + if (!validateForm()) { + return + } + + showProgressDialog() + val email = fieldEmail.text.toString() + val password = fieldPassword.text.toString() + + auth.signInWithEmailAndPassword(email, password) + .addOnCompleteListener(this) { task -> + Log.d(TAG, "signIn:onComplete:" + task.isSuccessful) + hideProgressDialog() + + if (task.isSuccessful) { + onAuthSuccess(task.result.user) + } else { + Toast.makeText(baseContext, "Sign In Failed", + Toast.LENGTH_SHORT).show() + } + } + } + + private fun signUp() { + Log.d(TAG, "signUp") + if (!validateForm()) { + return + } + + showProgressDialog() + val email = fieldEmail.text.toString() + val password = fieldPassword.text.toString() + + auth.createUserWithEmailAndPassword(email, password) + .addOnCompleteListener(this) { task -> + Log.d(TAG, "createUser:onComplete:" + task.isSuccessful) + hideProgressDialog() + + if (task.isSuccessful) { + onAuthSuccess(task.result.user) + } else { + Toast.makeText(baseContext, "Sign Up Failed", + Toast.LENGTH_SHORT).show() + } + } + } + + private fun onAuthSuccess(user: FirebaseUser) { + val username = usernameFromEmail(user.email!!) + + // Write new user + writeNewUser(user.uid, username, user.email) + + // Go to MainActivity + startActivity(Intent(this@SignInActivity, MainActivity::class.java)) + finish() + } + + private fun usernameFromEmail(email: String): String { + return if (email.contains("@")) { + email.split("@".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0] + } else { + email + } + } + + private fun validateForm(): Boolean { + var result = true + if (TextUtils.isEmpty(fieldEmail.text.toString())) { + fieldEmail.error = "Required" + result = false + } else { + fieldEmail.error = null + } + + if (TextUtils.isEmpty(fieldPassword.text.toString())) { + fieldPassword.error = "Required" + result = false + } else { + fieldPassword.error = null + } + + return result + } + + // [START basic_write] + private fun writeNewUser(userId: String, name: String, email: String?) { + val user = User(name, email) + database.child("users").child(userId).setValue(user) + } + // [END basic_write] + + override fun onClick(v: View) { + val i = v.id + if (i == R.id.buttonSignIn) { + signIn() + } else if (i == R.id.buttonSignUp) { + signUp() + } + } + + companion object { + + private const val TAG = "SignInActivity" + + } +} diff --git a/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/fragment/MyPostsFragment.kt b/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/fragment/MyPostsFragment.kt new file mode 100644 index 0000000000..ba2ab82500 --- /dev/null +++ b/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/fragment/MyPostsFragment.kt @@ -0,0 +1,15 @@ +package com.google.firebase.quickstart.database.kotlin.fragment + +import com.google.firebase.database.DatabaseReference +import com.google.firebase.database.Query + + +class MyPostsFragment : PostListFragment() { + + override fun getQuery(databaseReference: DatabaseReference): Query { + // All my posts + return databaseReference.child("user-posts") + .child(uid) + } + +} diff --git a/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/fragment/MyTopPostsFragment.kt b/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/fragment/MyTopPostsFragment.kt new file mode 100644 index 0000000000..b72523115d --- /dev/null +++ b/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/fragment/MyTopPostsFragment.kt @@ -0,0 +1,18 @@ +package com.google.firebase.quickstart.database.kotlin.fragment + +import com.google.firebase.database.DatabaseReference +import com.google.firebase.database.Query + + +class MyTopPostsFragment : PostListFragment() { + + override fun getQuery(databaseReference: DatabaseReference): Query { + // [START my_top_posts_query] + // My top posts by number of stars + val myUserId = uid + // [END my_top_posts_query] + + return databaseReference.child("user-posts").child(myUserId) + .orderByChild("starCount") + } +} diff --git a/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/fragment/PostListFragment.kt b/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/fragment/PostListFragment.kt new file mode 100644 index 0000000000..5fc2e33c51 --- /dev/null +++ b/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/fragment/PostListFragment.kt @@ -0,0 +1,153 @@ +package com.google.firebase.quickstart.database.kotlin.fragment + +import android.content.Intent +import android.os.Bundle +import android.support.v4.app.Fragment +import android.support.v7.widget.LinearLayoutManager +import android.support.v7.widget.RecyclerView +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.firebase.ui.database.FirebaseRecyclerAdapter +import com.firebase.ui.database.FirebaseRecyclerOptions +import com.google.firebase.auth.FirebaseAuth +import com.google.firebase.database.* +import com.google.firebase.quickstart.database.R +import com.google.firebase.quickstart.database.kotlin.PostDetailActivity +import com.google.firebase.quickstart.database.kotlin.models.Post +import com.google.firebase.quickstart.database.kotlin.viewholder.PostViewHolder +import kotlinx.android.synthetic.main.fragment_all_posts.view.* + + +abstract class PostListFragment : Fragment() { + + // [START define_database_reference] + private lateinit var database: DatabaseReference + // [END define_database_reference] + + private lateinit var recycler: RecyclerView + private lateinit var manager: LinearLayoutManager + private var adapter: FirebaseRecyclerAdapter? = null + + val uid: String + get() = FirebaseAuth.getInstance().currentUser!!.uid + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle?): View? { + super.onCreateView(inflater, container, savedInstanceState) + val rootView = inflater.inflate(R.layout.fragment_all_posts, container, false) + + // [START create_database_reference] + database = FirebaseDatabase.getInstance().reference + // [END create_database_reference] + + rootView.messagesList.setHasFixedSize(true) + + return rootView + } + + override fun onActivityCreated(savedInstanceState: Bundle?) { + super.onActivityCreated(savedInstanceState) + + // Set up Layout Manager, reverse layout + manager = LinearLayoutManager(activity) + manager.reverseLayout = true + manager.stackFromEnd = true + recycler.layoutManager = manager + + // Set up FirebaseRecyclerAdapter with the Query + val postsQuery = getQuery(database) + + val options = FirebaseRecyclerOptions.Builder() + .setQuery(postsQuery, Post::class.java) + .build() + + adapter = object : FirebaseRecyclerAdapter(options) { + + override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): PostViewHolder { + val inflater = LayoutInflater.from(viewGroup.context) + return PostViewHolder(inflater.inflate(R.layout.item_post, viewGroup, false)) + } + + override fun onBindViewHolder(viewHolder: PostViewHolder, position: Int, model: Post) { + val postRef = getRef(position) + + // Set click listener for the whole post view + val postKey = postRef.key + viewHolder.itemView.setOnClickListener { + // Launch PostDetailActivity + val intent = Intent(activity, PostDetailActivity::class.java) + intent.putExtra(PostDetailActivity.EXTRA_POST_KEY, postKey) + startActivity(intent) + } + + // Determine if the current user has liked this post and set UI accordingly + viewHolder.setLikedState(model.stars.containsKey(uid)) + + // Bind Post to ViewHolder, setting OnClickListener for the star button + viewHolder.bindToPost(model, View.OnClickListener { + // Need to write to both places the post is stored + val globalPostRef = database.child("posts").child(postRef.key!!) + val userPostRef = database.child("user-posts").child(model.uid!!).child(postRef.key!!) + + // Run two transactions + onStarClicked(globalPostRef) + onStarClicked(userPostRef) + }) + } + } + recycler.adapter = adapter + } + + // [START post_stars_transaction] + private fun onStarClicked(postRef: DatabaseReference) { + postRef.runTransaction(object : Transaction.Handler { + override fun doTransaction(mutableData: MutableData): Transaction.Result { + val p = mutableData.getValue(Post::class.java) + ?: return Transaction.success(mutableData) + + if (p.stars.containsKey(uid)) { + // Unstar the post and remove self from stars + p.starCount = p.starCount - 1 + p.stars.remove(uid) + } else { + // Star the post and add self to stars + p.starCount = p.starCount + 1 + p.stars[uid] = true + } + + // Set value and report transaction success + mutableData.value = p + return Transaction.success(mutableData) + } + + override fun onComplete(databaseError: DatabaseError?, b: Boolean, + dataSnapshot: DataSnapshot?) { + // Transaction completed + Log.d(TAG, "postTransaction:onComplete:" + databaseError!!) + } + }) + } + // [END post_stars_transaction] + + + override fun onStart() { + super.onStart() + adapter?.startListening() + } + + override fun onStop() { + super.onStop() + adapter?.stopListening() + } + + abstract fun getQuery(databaseReference: DatabaseReference): Query + + companion object { + + private const val TAG = "PostListFragment" + + } + +} diff --git a/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/fragment/RecentPostsFragment.kt b/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/fragment/RecentPostsFragment.kt new file mode 100644 index 0000000000..b48382f1e2 --- /dev/null +++ b/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/fragment/RecentPostsFragment.kt @@ -0,0 +1,16 @@ +package com.google.firebase.quickstart.database.kotlin.fragment + +import com.google.firebase.database.DatabaseReference +import com.google.firebase.database.Query + +class RecentPostsFragment : PostListFragment() { + + override fun getQuery(databaseReference: DatabaseReference): Query { + // [START recent_posts_query] + // Last 100 posts, these are automatically the 100 most recent + // due to sorting by push() keys. + return databaseReference.child("posts") + .limitToFirst(100) + // [END recent_posts_query] + } +} diff --git a/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/models/Comment.kt b/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/models/Comment.kt new file mode 100644 index 0000000000..92bf04dbdc --- /dev/null +++ b/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/models/Comment.kt @@ -0,0 +1,11 @@ +package com.google.firebase.quickstart.database.kotlin.models + +import com.google.firebase.database.IgnoreExtraProperties + +// [START comment_class] +@IgnoreExtraProperties +data class Comment( + var uid: String? = "", + var author: String? = "", + var text: String? = "") +// [END comment_class] diff --git a/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/models/Post.kt b/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/models/Post.kt new file mode 100644 index 0000000000..fcd83b9461 --- /dev/null +++ b/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/models/Post.kt @@ -0,0 +1,32 @@ +package com.google.firebase.quickstart.database.kotlin.models + +import com.google.firebase.database.Exclude +import com.google.firebase.database.IgnoreExtraProperties +import java.util.* + +// [START post_class] +@IgnoreExtraProperties +data class Post( + var uid: String? = "", + var author: String? = "", + var title: String? = "", + var body: String? = "", + var starCount: Int = 0, + var stars: MutableMap = HashMap()) { + + // [START post_to_map] + @Exclude + fun toMap(): Map { + return mapOf( + "uid" to uid, + "author" to author, + "title" to title, + "body" to body, + "starCount" to starCount, + "stars" to stars + ) + } + // [END post_to_map] + +} +// [END post_class] diff --git a/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/models/User.kt b/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/models/User.kt new file mode 100644 index 0000000000..3b055dd64d --- /dev/null +++ b/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/models/User.kt @@ -0,0 +1,9 @@ +package com.google.firebase.quickstart.database.kotlin.models + +import com.google.firebase.database.IgnoreExtraProperties + +// [START blog_user_class] +@IgnoreExtraProperties +data class User(var username: String? = "", + var email: String? = "") +// [END blog_user_class] diff --git a/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/viewholder/PostViewHolder.kt b/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/viewholder/PostViewHolder.kt new file mode 100644 index 0000000000..eb6bc6e105 --- /dev/null +++ b/database/app/src/main/java/com/google/firebase/quickstart/database/kotlin/viewholder/PostViewHolder.kt @@ -0,0 +1,30 @@ +package com.google.firebase.quickstart.database.kotlin.viewholder + +import android.support.v7.widget.RecyclerView +import android.view.View +import com.google.firebase.quickstart.database.R +import com.google.firebase.quickstart.database.kotlin.models.Post +import kotlinx.android.synthetic.main.include_post_author.view.* +import kotlinx.android.synthetic.main.include_post_text.view.* +import kotlinx.android.synthetic.main.item_post.view.* + +class PostViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + + fun bindToPost(post: Post, starClickListener: View.OnClickListener) { + itemView.postTitle.text = post.title + itemView.postAuthor.text = post.author + itemView.postNumStars.text = post.starCount.toString() + itemView.postBody.text = post.body + + itemView.star.setOnClickListener(starClickListener) + } + + fun setLikedState(liked: Boolean) { + if (liked) { + itemView.star.setImageResource(R.drawable.ic_toggle_star_24) + } else { + itemView.star.setImageResource(R.drawable.ic_toggle_star_outline_24) + } + } + +} diff --git a/database/app/src/main/res/layout/activity_main.xml b/database/app/src/main/res/layout/activity_main.xml index d3fb82495c..d754ce179a 100644 --- a/database/app/src/main/res/layout/activity_main.xml +++ b/database/app/src/main/res/layout/activity_main.xml @@ -2,8 +2,7 @@ + android:layout_height="match_parent"> + android:layout_height="match_parent"> + android:paddingTop="@dimen/activity_vertical_margin">