├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── firebase │ │ └── devday │ │ ├── BaseActivity.java │ │ ├── DetailActivity.java │ │ ├── ListActivity.java │ │ ├── MainActivity.java │ │ ├── PetrolActivity.java │ │ ├── ReadWriteActivity.java │ │ ├── SigninActivity.java │ │ ├── configs │ │ ├── MyAppGlideModule.java │ │ └── MyApplication.java │ │ ├── helpers │ │ └── MyHelper.java │ │ └── models │ │ ├── Message.java │ │ ├── Topic.java │ │ └── User.java │ └── res │ ├── drawable-hdpi │ └── ic_check.png │ ├── drawable-xhdpi │ └── ic_check.png │ ├── drawable-xxhdpi │ └── ic_check.png │ ├── drawable-xxxhdpi │ └── ic_check.png │ ├── layout │ ├── activity_detail.xml │ ├── activity_main.xml │ ├── activity_petrol.xml │ ├── activity_read_write.xml │ ├── activity_signin.xml │ ├── fragment_all_topics.xml │ └── item_topic.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 │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | #built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Local configuration file (sdk path, etc) 17 | local.properties 18 | 19 | # Windows thumbnail db 20 | Thumbs.db 21 | 22 | # OSX files 23 | .DS_Store 24 | 25 | # Log Files 26 | *.log 27 | 28 | # Android Studio 29 | *.iml 30 | .gradle/ 31 | build/ 32 | captures/ 33 | .navigation/ 34 | 35 | # Intellij IDEA 36 | .idea/ 37 | 38 | # Eclipse project files 39 | .classpath 40 | .project 41 | 42 | # Proguard folder generated by Eclipse 43 | proguard/ 44 | 45 | # Eclipse Metadata 46 | .metadata/ 47 | 48 | # Keystore files 49 | *.jks 50 | 51 | .externalNativeBuild 52 | 53 | google-services.json 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deep Dive into Realtime Database (Firebase Dev Day 2017) 2 | Sample code from Deep dive into Firebase Realtime Database session in Firebase Dev Day 2017 Bangkok 3 | 4 | ## Prerequisites 5 | * Supported Android 4.1 or newer 6 | * Android Studio 3.3.2 or higher 7 | * google-services.json in app-level folder 8 | 9 | ## Contents 10 | * DatabaseReference 11 | * Data Reading 12 | * Data Writing 13 | * Listeners 14 | * Query Ordering 15 | * Query Filtering 16 | * Data Removing 17 | * Enabling Offline Mode 18 | 19 | ## Screenshots 20 | 21 | 22 | 23 | 24 |
25 | 26 | ## Blog 27 | [Deep Dive into Firebase Realtime Database](https://medium.com/@jirawatee/deep-dive-into-firebase-realtime-database-5cc692998375#.nushk7csb) 28 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion compileAndTargetSdk 5 | buildToolsVersion "30.0.3" 6 | 7 | defaultConfig { 8 | applicationId "com.firebase.devday" 9 | minSdkVersion 21 10 | targetSdkVersion compileAndTargetSdk 11 | versionCode 1 12 | versionName "1.0" 13 | resConfigs("en", "xxxhdpi") 14 | ndk { 15 | abiFilters "x86", "x86_64", "arm64-v8a", "armeabi-v7a" 16 | } 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled true 22 | shrinkResources true 23 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 24 | } 25 | debug { 26 | splits.abi.enable = false 27 | splits.density.enable = false 28 | aaptOptions.cruncherEnabled = false 29 | } 30 | } 31 | 32 | dexOptions { 33 | preDexLibraries true 34 | maxProcessCount 8 35 | } 36 | } 37 | 38 | dependencies { 39 | implementation 'androidx.appcompat:appcompat:1.2.0' 40 | implementation 'androidx.recyclerview:recyclerview:1.2.0' 41 | implementation 'androidx.cardview:cardview:1.0.0' 42 | implementation 'com.google.android.material:material:1.3.0' 43 | implementation "com.google.android.gms:play-services-auth:19.0.0" 44 | implementation "com.google.firebase:firebase-auth:20.0.4" 45 | implementation "com.google.firebase:firebase-database:19.7.0" 46 | implementation "com.github.bumptech.glide:glide:$glideLibraryVersion" 47 | annotationProcessor "com.github.bumptech.glide:compiler:$glideLibraryVersion" 48 | annotationProcessor 'androidx.annotation:annotation:1.2.0' 49 | } 50 | 51 | apply plugin: 'com.google.gms.google-services' -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | -keepattributes Signature 2 | -keepattributes *Annotation* 3 | -keepattributes EnclosingMethod 4 | -keepattributes InnerClasses 5 | 6 | -keepclassmembers class com.firebase.devday.models.** {*;} 7 | 8 | -keep public class * implements com.bumptech.glide.module.GlideModule 9 | -keep public class * extends com.bumptech.glide.module.AppGlideModule 10 | -keep public enum com.bumptech.glide.load.ImageHeaderParser$** { 11 | **[] $VALUES; 12 | public *; 13 | } 14 | 15 | # If you're targeting any API level less than Android API 27, also include: 16 | -dontwarn com.bumptech.glide.load.resource.bitmap.VideoDecoder 17 | 18 | -assumenosideeffects class android.util.Log { 19 | public static boolean isLoggable(java.lang.String, int); 20 | public static int v(...); 21 | public static int i(...); 22 | public static int w(...); 23 | public static int d(...); 24 | public static int e(...); 25 | public static int wtf(...); 26 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 15 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/java/com/firebase/devday/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.firebase.devday; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | import android.widget.Toast; 5 | 6 | public class BaseActivity extends AppCompatActivity { 7 | private long mBackPressed = 0; 8 | 9 | @Override 10 | public void onBackPressed() { 11 | if (mBackPressed + 2000 > System.currentTimeMillis()) { 12 | super.onBackPressed(); 13 | } else { 14 | Toast.makeText(this, R.string.exit, Toast.LENGTH_SHORT).show(); 15 | mBackPressed = System.currentTimeMillis(); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/firebase/devday/DetailActivity.java: -------------------------------------------------------------------------------- 1 | package com.firebase.devday; 2 | 3 | import android.os.Bundle; 4 | import androidx.appcompat.app.AppCompatActivity; 5 | import android.widget.ImageView; 6 | import android.widget.TextView; 7 | 8 | import com.bumptech.glide.Glide; 9 | import com.firebase.devday.helpers.MyHelper; 10 | import com.firebase.devday.models.Topic; 11 | import com.google.firebase.database.DataSnapshot; 12 | import com.google.firebase.database.DatabaseError; 13 | import com.google.firebase.database.DatabaseReference; 14 | import com.google.firebase.database.FirebaseDatabase; 15 | import com.google.firebase.database.ValueEventListener; 16 | 17 | public class DetailActivity extends AppCompatActivity { 18 | private DatabaseReference mTopicRef; 19 | private ImageView mImageView; 20 | private TextView mTextView; 21 | private ValueEventListener valueEventListener; 22 | 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | setContentView(R.layout.activity_detail); 27 | 28 | mImageView = findViewById(R.id.picture); 29 | mTextView = findViewById(R.id.txt); 30 | 31 | String topicKey = getIntent().getStringExtra("topicKey"); 32 | 33 | DatabaseReference mDatabase = FirebaseDatabase.getInstance().getReference(); 34 | mTopicRef = mDatabase.child("topics").child(topicKey); 35 | } 36 | 37 | @Override 38 | protected void onStart() { 39 | super.onStart(); 40 | MyHelper.showDialog(this); 41 | valueEventListener = new ValueEventListener() { 42 | @Override 43 | public void onDataChange(DataSnapshot dataSnapshot) { 44 | MyHelper.dismissDialog(); 45 | Topic topic = dataSnapshot.getValue(Topic.class); 46 | updateView(topic); 47 | } 48 | 49 | @Override 50 | public void onCancelled(DatabaseError databaseError) { 51 | MyHelper.dismissDialog(); 52 | } 53 | }; 54 | mTopicRef.addValueEventListener(valueEventListener); 55 | } 56 | 57 | @Override 58 | protected void onStop() { 59 | super.onStop(); 60 | if (valueEventListener != null) { 61 | mTopicRef.removeEventListener(valueEventListener); 62 | } 63 | } 64 | 65 | private void updateView(Topic topic) { 66 | Glide.with(this).load(topic.picture).into(mImageView); 67 | mTextView.setText(topic.title); 68 | mTextView.append("\n\n" + topic.speaker); 69 | mTextView.append("\n\n" + topic.body); 70 | mTextView.append("\n\nVote " + topic.voteCount); 71 | } 72 | } -------------------------------------------------------------------------------- /app/src/main/java/com/firebase/devday/ListActivity.java: -------------------------------------------------------------------------------- 1 | package com.firebase.devday; 2 | 3 | import android.content.Context; 4 | import android.content.DialogInterface; 5 | import android.content.Intent; 6 | import android.os.Bundle; 7 | import androidx.appcompat.app.AlertDialog; 8 | import androidx.appcompat.app.AppCompatActivity; 9 | import androidx.recyclerview.widget.LinearLayoutManager; 10 | import androidx.recyclerview.widget.RecyclerView; 11 | import android.util.Log; 12 | import android.view.LayoutInflater; 13 | import android.view.View; 14 | import android.view.ViewGroup; 15 | import android.widget.ImageView; 16 | import android.widget.TextView; 17 | import android.widget.Toast; 18 | 19 | import com.firebase.devday.configs.GlideApp; 20 | import com.firebase.devday.helpers.MyHelper; 21 | import com.firebase.devday.models.Topic; 22 | import com.google.firebase.auth.FirebaseAuth; 23 | import com.google.firebase.auth.FirebaseUser; 24 | import com.google.firebase.database.ChildEventListener; 25 | import com.google.firebase.database.DataSnapshot; 26 | import com.google.firebase.database.DatabaseError; 27 | import com.google.firebase.database.DatabaseReference; 28 | import com.google.firebase.database.FirebaseDatabase; 29 | import com.google.firebase.database.Query; 30 | import com.google.firebase.database.ValueEventListener; 31 | 32 | import java.util.ArrayList; 33 | import java.util.HashMap; 34 | import java.util.List; 35 | import java.util.Map; 36 | 37 | public class ListActivity extends AppCompatActivity implements View.OnClickListener{ 38 | private static final String TAG = "ListActivity"; 39 | private Query mQuery; 40 | private RecyclerView mRecycler; 41 | private TopicAdapter mAdapter; 42 | private DatabaseReference mDatabase; 43 | 44 | @Override 45 | protected void onCreate(Bundle savedInstanceState) { 46 | super.onCreate(savedInstanceState); 47 | setContentView(R.layout.fragment_all_topics); 48 | findViewById(R.id.fab).setOnClickListener(this); 49 | 50 | mRecycler = findViewById(R.id.topic_list); 51 | mRecycler.setHasFixedSize(true); 52 | LinearLayoutManager mManager = new LinearLayoutManager(this); 53 | //mManager.setReverseLayout(true); 54 | //mManager.setStackFromEnd(true); 55 | mRecycler.setLayoutManager(mManager); 56 | 57 | mDatabase = FirebaseDatabase.getInstance().getReference(); 58 | 59 | /* Ordering */ 60 | mQuery = mDatabase.child("topics"); 61 | // orderByKey() 62 | //mQuery = mQuery.orderByChild("voteCount"); 63 | 64 | 65 | /* Filtering */ 66 | //mQuery = mQuery.limitToFirst(3); 67 | //mQuery = mQuery.limitToLast(3); 68 | //mQuery = mQuery.equalTo("Firebase Dev Day BKK"); 69 | //mQuery = mQuery.startAt("Firebase Realtime Database"); 70 | //mQuery = mQuery.startAt(50); 71 | //mQuery = mQuery.endAt(50); 72 | //mQuery = mQuery.startAt(50).limitToFirst(2); 73 | } 74 | 75 | @Override 76 | protected void onStart() { 77 | super.onStart(); 78 | mAdapter = new TopicAdapter(this, mQuery); 79 | mRecycler.setAdapter(mAdapter); 80 | } 81 | 82 | @Override 83 | public void onStop() { 84 | super.onStop(); 85 | mAdapter.cleanupListener(); 86 | } 87 | 88 | @Override 89 | public void onClick(View view) { 90 | switch (view.getId()) { 91 | case R.id.fab: 92 | FirebaseUser firebaser = FirebaseAuth.getInstance().getCurrentUser(); 93 | 94 | String uid = firebaser.getUid(); 95 | String avatar = firebaser.getPhotoUrl().toString(); 96 | String speaker = "Jirawatee"; 97 | String picture = "https://pbs.twimg.com/media/C4yDZ6QUkAEJ0ZW.jpg"; 98 | String title = "Hello World!"; 99 | String body = "Geek Alert!"; 100 | 101 | final Topic topic = new Topic(uid, speaker, avatar, title, body, picture); 102 | Map topicValues = topic.toMap(); 103 | Map childUpdates = new HashMap<>(); 104 | 105 | String topicKey = mDatabase.push().getKey(); 106 | childUpdates.put("/topics/" + topicKey, topicValues); 107 | childUpdates.put("/user-topics/" + uid + "/" + topicKey, topicValues); 108 | 109 | MyHelper.showDialog(this); 110 | mDatabase.updateChildren(childUpdates, new DatabaseReference.CompletionListener() { 111 | @Override 112 | public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) { 113 | MyHelper.dismissDialog(); 114 | if (databaseError != null) { 115 | Toast.makeText(ListActivity.this, databaseError.getMessage(), Toast.LENGTH_SHORT).show(); 116 | } else { 117 | Toast.makeText(ListActivity.this, "Added!", Toast.LENGTH_SHORT).show(); 118 | } 119 | } 120 | }); 121 | break; 122 | } 123 | } 124 | 125 | private static class TopicViewHolder extends RecyclerView.ViewHolder { 126 | ImageView avatar; 127 | TextView titleView, speakerView, voteView; 128 | TopicViewHolder(View itemView) { 129 | super(itemView); 130 | avatar = itemView.findViewById(R.id.avatar); 131 | titleView = itemView.findViewById(R.id.title); 132 | speakerView = itemView.findViewById(R.id.speaker); 133 | voteView = itemView.findViewById(R.id.vote); 134 | } 135 | } 136 | 137 | private class TopicAdapter extends RecyclerView.Adapter { 138 | private Context mContext; 139 | private Query mDatabaseReference; 140 | private ValueEventListener mValueEventListener; 141 | private ChildEventListener mChildEventListener; 142 | private List mTopicIds = new ArrayList<>(); 143 | private List mTopics = new ArrayList<>(); 144 | 145 | TopicAdapter(Context context, Query query) { 146 | mContext = context; 147 | mDatabaseReference = query; 148 | MyHelper.showDialog(mContext); 149 | mChildEventListener = new ChildEventListener() { 150 | @Override 151 | public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) { 152 | MyHelper.dismissDialog(); 153 | 154 | Topic topic = dataSnapshot.getValue(Topic.class); 155 | Log.d("onChildAdded", topic.voteCount + ": " + topic.title); 156 | 157 | mTopicIds.add(dataSnapshot.getKey()); 158 | mTopics.add(topic); 159 | notifyItemInserted(mTopics.size() - 1); 160 | } 161 | 162 | @Override 163 | public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) { 164 | MyHelper.dismissDialog(); 165 | 166 | Topic newTopic = dataSnapshot.getValue(Topic.class); 167 | Log.d("onChildChanged", newTopic.title); 168 | 169 | String topicKey = dataSnapshot.getKey(); 170 | int topicIndex = mTopicIds.indexOf(topicKey); 171 | if (topicIndex > -1) { 172 | mTopics.set(topicIndex, newTopic); 173 | notifyItemChanged(topicIndex); 174 | } else { 175 | Log.w(TAG, "onChildChanged:unknown_child:" + topicKey); 176 | } 177 | } 178 | 179 | @Override 180 | public void onChildRemoved(DataSnapshot dataSnapshot) { 181 | MyHelper.dismissDialog(); 182 | Log.d("onChildRemoved", dataSnapshot.getKey()); 183 | 184 | String topicKey = dataSnapshot.getKey(); 185 | int topicIndex = mTopicIds.indexOf(topicKey); 186 | if (topicIndex > -1) { 187 | mTopicIds.remove(topicIndex); 188 | mTopics.remove(topicIndex); 189 | notifyItemRemoved(topicIndex); 190 | } else { 191 | Log.w(TAG, "onChildRemoved:unknown_child:" + topicKey); 192 | } 193 | } 194 | 195 | @Override 196 | public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) { 197 | MyHelper.dismissDialog(); 198 | Log.d("onChildMoved", dataSnapshot.getKey()); 199 | } 200 | 201 | @Override 202 | public void onCancelled(DatabaseError databaseError) { 203 | MyHelper.dismissDialog(); 204 | Log.w(TAG, databaseError.toException()); 205 | Toast.makeText(mContext, "Failed to load topics.", Toast.LENGTH_SHORT).show(); 206 | } 207 | }; 208 | query.addChildEventListener(mChildEventListener); 209 | } 210 | 211 | @Override 212 | public TopicViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 213 | LayoutInflater inflater = LayoutInflater.from(mContext); 214 | View view = inflater.inflate(R.layout.item_topic, parent, false); 215 | return new TopicViewHolder(view); 216 | } 217 | 218 | @Override 219 | public void onBindViewHolder(final TopicViewHolder holder, final int position) { 220 | final Topic topic = mTopics.get(position); 221 | holder.titleView.setText(topic.title); 222 | holder.speakerView.setText(topic.speaker); 223 | holder.voteView.setText(mContext.getString(R.string.vote, topic.voteCount)); 224 | GlideApp.with(mContext).load(topic.avatar).circleCrop().error(R.mipmap.ic_launcher_round).into(holder.avatar); 225 | 226 | holder.itemView.setOnClickListener(new View.OnClickListener() { 227 | @Override 228 | public void onClick(View view) { 229 | Intent intent = new Intent(ListActivity.this, DetailActivity.class); 230 | intent.putExtra("topicKey", mTopicIds.get(holder.getAdapterPosition())); 231 | startActivity(intent); 232 | } 233 | }); 234 | 235 | holder.itemView.setOnLongClickListener(new View.OnLongClickListener() { 236 | @Override 237 | public boolean onLongClick(View view) { 238 | String topicKey = mTopicIds.get(holder.getAdapterPosition()); 239 | confirmDelete(topicKey, topic); 240 | return true; 241 | } 242 | }); 243 | } 244 | 245 | @Override 246 | public int getItemCount() { 247 | return mTopics.size(); 248 | } 249 | 250 | void cleanupListener() { 251 | if (mValueEventListener != null) { 252 | mDatabaseReference.removeEventListener(mValueEventListener); 253 | } 254 | if (mChildEventListener != null) { 255 | mDatabaseReference.removeEventListener(mChildEventListener); 256 | } 257 | } 258 | 259 | private void confirmDelete(final String topicKey, final Topic topic) { 260 | AlertDialog.Builder alert = new AlertDialog.Builder(mContext); 261 | alert.setMessage(getString(R.string.confirm_delete, topic.title)); 262 | alert.setPositiveButton("Yes", new DialogInterface.OnClickListener() { 263 | @Override 264 | public void onClick(DialogInterface dialogInterface, int i) { 265 | dialogInterface.dismiss(); 266 | //mDatabase.child("topics").child(topicKey).setValue(null); 267 | //mDatabase.child("user-topics").child(topic.uid).child(topicKey).removeValue(); 268 | 269 | Map childUpdates = new HashMap<>(); 270 | childUpdates.put("/topics/" + topicKey, null); 271 | childUpdates.put("/user-topics/" + topic.uid + "/" + topicKey, null); 272 | FirebaseDatabase.getInstance().getReference().updateChildren(childUpdates); 273 | } 274 | }); 275 | alert.setNegativeButton("No", new DialogInterface.OnClickListener() { 276 | @Override 277 | public void onClick(DialogInterface dialogInterface, int i) { 278 | dialogInterface.dismiss(); 279 | } 280 | }); 281 | alert.setCancelable(true); 282 | alert.show(); 283 | } 284 | } 285 | } -------------------------------------------------------------------------------- /app/src/main/java/com/firebase/devday/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.firebase.devday; 2 | 3 | import android.content.DialogInterface; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import androidx.appcompat.app.AlertDialog; 7 | import android.view.View; 8 | 9 | import com.firebase.devday.helpers.MyHelper; 10 | import com.google.firebase.auth.FirebaseAuth; 11 | 12 | public class MainActivity extends BaseActivity implements View.OnClickListener { 13 | @Override 14 | protected void onCreate(Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | setContentView(R.layout.activity_main); 17 | 18 | findViewById(R.id.btn_read_write).setOnClickListener(this); 19 | findViewById(R.id.btn_orderby_value).setOnClickListener(this); 20 | findViewById(R.id.btn_listener).setOnClickListener(this); 21 | findViewById(R.id.btn_sign_out).setOnClickListener(this); 22 | } 23 | 24 | @Override 25 | public void onClick(View view) { 26 | switch (view.getId()) { 27 | case R.id.btn_read_write: 28 | startActivity(new Intent(this, ReadWriteActivity.class)); 29 | break; 30 | case R.id.btn_listener: 31 | startActivity(new Intent(this, ListActivity.class)); 32 | break; 33 | case R.id.btn_orderby_value: 34 | startActivity(new Intent(this, PetrolActivity.class)); 35 | break; 36 | case R.id.btn_sign_out: 37 | signOut(); 38 | break; 39 | } 40 | } 41 | 42 | private void signOut() { 43 | AlertDialog.Builder alert = new AlertDialog.Builder(this); 44 | alert.setMessage("Sign out?"); 45 | alert.setPositiveButton("Yes", new DialogInterface.OnClickListener() { 46 | @Override 47 | public void onClick(DialogInterface dialogInterface, int i) { 48 | dialogInterface.dismiss(); 49 | FirebaseAuth.getInstance().signOut(); 50 | MyHelper.redirect(MainActivity.this, SigninActivity.class); 51 | } 52 | }); 53 | alert.setNegativeButton("No", new DialogInterface.OnClickListener() { 54 | @Override 55 | public void onClick(DialogInterface dialogInterface, int i) { 56 | dialogInterface.dismiss(); 57 | } 58 | }); 59 | alert.show(); 60 | } 61 | } -------------------------------------------------------------------------------- /app/src/main/java/com/firebase/devday/PetrolActivity.java: -------------------------------------------------------------------------------- 1 | package com.firebase.devday; 2 | 3 | import android.os.Bundle; 4 | import androidx.appcompat.app.AppCompatActivity; 5 | import android.util.Log; 6 | import android.widget.TextView; 7 | 8 | import com.firebase.devday.helpers.MyHelper; 9 | import com.google.firebase.database.DataSnapshot; 10 | import com.google.firebase.database.DatabaseError; 11 | import com.google.firebase.database.DatabaseReference; 12 | import com.google.firebase.database.FirebaseDatabase; 13 | import com.google.firebase.database.Query; 14 | import com.google.firebase.database.ValueEventListener; 15 | 16 | public class PetrolActivity extends AppCompatActivity { 17 | private Query mQuery; 18 | private TextView mTextView; 19 | private ValueEventListener valueEventListener; 20 | 21 | @Override 22 | protected void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | setContentView(R.layout.activity_petrol); 25 | mTextView = findViewById(R.id.txt); 26 | DatabaseReference mDatabase = FirebaseDatabase.getInstance().getReference(); 27 | mQuery = mDatabase.child("Petrol").orderByValue(); 28 | } 29 | 30 | @Override 31 | protected void onStart() { 32 | super.onStart(); 33 | MyHelper.showDialog(this); 34 | valueEventListener = new ValueEventListener() { 35 | @Override 36 | public void onDataChange(DataSnapshot dataSnapshot) { 37 | MyHelper.dismissDialog(); 38 | Iterable children = dataSnapshot.getChildren(); 39 | while(children.iterator().hasNext()) { 40 | String key = children.iterator().next().getKey(); 41 | mTextView.append(dataSnapshot.getKey()); 42 | mTextView.append(": " + dataSnapshot.child(key).getValue(Double.class)); 43 | mTextView.append("\n"); 44 | 45 | Log.d("Petrol", dataSnapshot.getKey() + ": " + dataSnapshot.child(key).getValue(Double.class)); 46 | } 47 | } 48 | 49 | @Override 50 | public void onCancelled(DatabaseError databaseError) { 51 | MyHelper.dismissDialog(); 52 | } 53 | }; 54 | mQuery.addValueEventListener(valueEventListener); 55 | } 56 | 57 | @Override 58 | protected void onStop() { 59 | super.onStop(); 60 | if (valueEventListener != null) { 61 | mQuery.removeEventListener(valueEventListener); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /app/src/main/java/com/firebase/devday/ReadWriteActivity.java: -------------------------------------------------------------------------------- 1 | package com.firebase.devday; 2 | 3 | import android.os.Bundle; 4 | import androidx.appcompat.app.AppCompatActivity; 5 | import android.view.View; 6 | import android.widget.TextView; 7 | 8 | import com.firebase.devday.helpers.MyHelper; 9 | import com.firebase.devday.models.User; 10 | import com.google.firebase.database.DataSnapshot; 11 | import com.google.firebase.database.DatabaseError; 12 | import com.google.firebase.database.DatabaseReference; 13 | import com.google.firebase.database.FirebaseDatabase; 14 | import com.google.firebase.database.ValueEventListener; 15 | 16 | public class ReadWriteActivity extends AppCompatActivity implements View.OnClickListener { 17 | private static final String USER_ID = "HuwtqNSpuedOz9rprj4YLPID2oy2"; 18 | private DatabaseReference mDatabase, mUsersRef; 19 | private TextView mTextView; 20 | private ValueEventListener valueEventListener; 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_read_write); 26 | 27 | mTextView = findViewById(R.id.txt); 28 | findViewById(R.id.btn_set_single).setOnClickListener(this); 29 | findViewById(R.id.btn_set_object).setOnClickListener(this); 30 | findViewById(R.id.btn_push).setOnClickListener(this); 31 | 32 | mDatabase = FirebaseDatabase.getInstance().getReference(); 33 | mUsersRef = mDatabase.child("users").child(USER_ID); 34 | } 35 | 36 | @Override 37 | protected void onStart() { 38 | super.onStart(); 39 | MyHelper.showDialog(this); 40 | valueEventListener = new ValueEventListener() { 41 | @Override 42 | public void onDataChange(DataSnapshot dataSnapshot) { 43 | MyHelper.dismissDialog(); 44 | User user = dataSnapshot.getValue(User.class); 45 | updateView(user); 46 | } 47 | 48 | @Override 49 | public void onCancelled(DatabaseError databaseError) { 50 | MyHelper.dismissDialog(); 51 | } 52 | }; 53 | mUsersRef.addValueEventListener(valueEventListener); 54 | } 55 | 56 | @Override 57 | protected void onStop() { 58 | super.onStop(); 59 | if (valueEventListener != null) { 60 | mUsersRef.removeEventListener(valueEventListener); 61 | } 62 | } 63 | 64 | @Override 65 | public void onClick(View view) { 66 | User user = new User( 67 | "firebasethailand@gmail.com", 68 | "Firebaser", 69 | "https://pbs.twimg.com/profile_images/744165523978493952/02eZ4I34.jpg", 70 | true 71 | ); 72 | switch (view.getId()) { 73 | case R.id.btn_set_single: 74 | mUsersRef.child("email").setValue("firebasethailand@gmail.com"); 75 | break; 76 | case R.id.btn_set_object: 77 | mUsersRef.setValue(user); 78 | break; 79 | case R.id.btn_push: 80 | mDatabase.child("users").push().setValue(user); 81 | break; 82 | } 83 | } 84 | 85 | private void updateView(User user) { 86 | mTextView.setText(user.email); 87 | mTextView.append("\n" + user.speaker); 88 | mTextView.append("\n" + user.isAdmin); 89 | } 90 | } -------------------------------------------------------------------------------- /app/src/main/java/com/firebase/devday/SigninActivity.java: -------------------------------------------------------------------------------- 1 | package com.firebase.devday; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import androidx.annotation.NonNull; 6 | import android.view.View; 7 | import android.widget.Toast; 8 | 9 | import com.firebase.devday.helpers.MyHelper; 10 | import com.firebase.devday.models.User; 11 | import com.google.android.gms.auth.api.Auth; 12 | import com.google.android.gms.auth.api.signin.GoogleSignInAccount; 13 | import com.google.android.gms.auth.api.signin.GoogleSignInOptions; 14 | import com.google.android.gms.auth.api.signin.GoogleSignInResult; 15 | import com.google.android.gms.common.ConnectionResult; 16 | import com.google.android.gms.common.SignInButton; 17 | import com.google.android.gms.common.api.GoogleApiClient; 18 | import com.google.android.gms.tasks.OnCompleteListener; 19 | import com.google.android.gms.tasks.Task; 20 | import com.google.firebase.auth.AuthCredential; 21 | import com.google.firebase.auth.AuthResult; 22 | import com.google.firebase.auth.FirebaseAuth; 23 | import com.google.firebase.auth.FirebaseUser; 24 | import com.google.firebase.auth.GoogleAuthProvider; 25 | import com.google.firebase.database.FirebaseDatabase; 26 | 27 | public class SigninActivity extends BaseActivity implements View.OnClickListener, GoogleApiClient.OnConnectionFailedListener { 28 | private static final int RC_SIGN_IN = 9001; 29 | private FirebaseAuth mAuth; 30 | private GoogleApiClient mGoogleApiClient; 31 | 32 | @Override 33 | protected void onCreate(Bundle savedInstanceState) { 34 | super.onCreate(savedInstanceState); 35 | setContentView(R.layout.activity_signin); 36 | mAuth = FirebaseAuth.getInstance(); 37 | 38 | SignInButton signInButton = findViewById(R.id.sign_in_button); 39 | signInButton.setSize(SignInButton.SIZE_WIDE); 40 | signInButton.setOnClickListener(this); 41 | } 42 | 43 | @Override 44 | protected void onStart() { 45 | super.onStart(); 46 | if (mAuth.getCurrentUser() != null) { 47 | MyHelper.redirect(this, MainActivity.class); 48 | } 49 | } 50 | 51 | @Override 52 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 53 | super.onActivityResult(requestCode, resultCode, data); 54 | if (resultCode == RESULT_OK && requestCode == RC_SIGN_IN) { 55 | GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data); 56 | if (result.isSuccess()) { 57 | firebaseAuthWithGoogle(result.getSignInAccount()); 58 | } else { 59 | Toast.makeText(this, result.getStatus().getStatusMessage(), Toast.LENGTH_SHORT).show(); 60 | } 61 | } 62 | } 63 | 64 | @Override 65 | public void onClick(View view) { 66 | if (view.getId() == R.id.sign_in_button) { 67 | if (mGoogleApiClient == null) { 68 | mGoogleApiClient = buildGoogleSignIn(); 69 | } 70 | Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient); 71 | startActivityForResult(signInIntent, RC_SIGN_IN); 72 | } 73 | } 74 | 75 | @Override 76 | public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { 77 | Toast.makeText(this, connectionResult.getErrorMessage(), Toast.LENGTH_SHORT).show(); 78 | } 79 | 80 | private void firebaseAuthWithGoogle(GoogleSignInAccount acct) { 81 | MyHelper.showDialog(this); 82 | AuthCredential credential = GoogleAuthProvider.getCredential(acct.getIdToken(), null); 83 | mAuth.signInWithCredential(credential).addOnCompleteListener(this, new OnCompleteListener() { 84 | @Override 85 | public void onComplete(@NonNull Task task) { 86 | if (task.isSuccessful()) { 87 | FirebaseUser firebaser = mAuth.getCurrentUser(); 88 | User user = new User( 89 | firebaser.getEmail(), 90 | firebaser.getDisplayName(), 91 | firebaser.getPhotoUrl().toString(), 92 | true 93 | ); 94 | FirebaseDatabase.getInstance().getReference().child("users").child(firebaser.getUid()).setValue(user); 95 | MyHelper.dismissDialog(); 96 | MyHelper.redirect(SigninActivity.this, MainActivity.class); 97 | } else { 98 | MyHelper.dismissDialog(); 99 | //noinspection ThrowableResultOfMethodCallIgnored,ConstantConditions 100 | Toast.makeText(SigninActivity.this, task.getException().getMessage(), Toast.LENGTH_SHORT).show(); 101 | } 102 | } 103 | }); 104 | } 105 | 106 | public GoogleApiClient buildGoogleSignIn() { 107 | GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) 108 | .requestIdToken(getString(R.string.default_web_client_id)) 109 | .requestEmail() 110 | .build(); 111 | return new GoogleApiClient.Builder(this) 112 | .enableAutoManage(this, this) 113 | .addApi(Auth.GOOGLE_SIGN_IN_API, gso) 114 | .build(); 115 | } 116 | } -------------------------------------------------------------------------------- /app/src/main/java/com/firebase/devday/configs/MyAppGlideModule.java: -------------------------------------------------------------------------------- 1 | package com.firebase.devday.configs; 2 | 3 | import com.bumptech.glide.annotation.GlideModule; 4 | import com.bumptech.glide.module.AppGlideModule; 5 | 6 | @GlideModule 7 | public final class MyAppGlideModule extends AppGlideModule { 8 | @Override 9 | public boolean isManifestParsingEnabled() { 10 | return false; 11 | } 12 | } -------------------------------------------------------------------------------- /app/src/main/java/com/firebase/devday/configs/MyApplication.java: -------------------------------------------------------------------------------- 1 | package com.firebase.devday.configs; 2 | 3 | import android.app.Application; 4 | 5 | import com.google.firebase.database.FirebaseDatabase; 6 | 7 | public class MyApplication extends Application{ 8 | @Override 9 | public void onCreate() { 10 | super.onCreate(); 11 | FirebaseDatabase.getInstance().setPersistenceEnabled(true); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/java/com/firebase/devday/helpers/MyHelper.java: -------------------------------------------------------------------------------- 1 | package com.firebase.devday.helpers; 2 | 3 | import android.app.Activity; 4 | import android.app.Dialog; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.widget.LinearLayout; 8 | import android.widget.ProgressBar; 9 | 10 | import com.firebase.devday.R; 11 | 12 | public class MyHelper { 13 | private static Dialog mDialog; 14 | 15 | public static void showDialog(Context context) { 16 | mDialog = new Dialog(context, R.style.CustomDialog); 17 | mDialog.addContentView( 18 | new ProgressBar(context), 19 | new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT) 20 | ); 21 | mDialog.setCancelable(true); 22 | if (!mDialog.isShowing()) { 23 | mDialog.show(); 24 | } 25 | } 26 | 27 | public static void dismissDialog() { 28 | if (mDialog != null && mDialog.isShowing()) { 29 | mDialog.dismiss(); 30 | } 31 | } 32 | 33 | public static void redirect(Activity activity, Class activityClass) { 34 | activity.startActivity(new Intent(activity, activityClass)); 35 | activity.finish(); 36 | } 37 | } -------------------------------------------------------------------------------- /app/src/main/java/com/firebase/devday/models/Message.java: -------------------------------------------------------------------------------- 1 | package com.firebase.devday.models; 2 | 3 | import com.google.firebase.database.IgnoreExtraProperties; 4 | 5 | @IgnoreExtraProperties 6 | public class Message { 7 | public String message; 8 | public boolean translated; 9 | 10 | public Message() { 11 | // Default constructor required for calls to DataSnapshot.getValue(User.class) 12 | } 13 | 14 | public Message(String message, boolean translated) { 15 | this.message = message; 16 | this.translated = translated; 17 | } 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/firebase/devday/models/Topic.java: -------------------------------------------------------------------------------- 1 | package com.firebase.devday.models; 2 | 3 | import com.google.firebase.database.Exclude; 4 | import com.google.firebase.database.IgnoreExtraProperties; 5 | import com.google.firebase.database.ServerValue; 6 | 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | 10 | @IgnoreExtraProperties 11 | public class Topic { 12 | public String avatar; 13 | public String body; 14 | public String picture; 15 | public String speaker; 16 | public String status = "future"; 17 | public long timeAdd = 0; 18 | public String title; 19 | public String uid; 20 | 21 | public int voteCount = 0; 22 | public Map votes = new HashMap<>(); 23 | 24 | public int joinCount = 0; 25 | public Map joins = new HashMap<>(); 26 | 27 | public int redeemCount = 0; 28 | 29 | public Topic() { 30 | // Default constructor required for calls to DataSnapshot.getValue(Topic.class) 31 | } 32 | 33 | public Topic(String uid, String speaker, String avatar, String title, String body, String picture) { 34 | this.uid = uid; 35 | this.speaker = speaker; 36 | this.avatar = avatar; 37 | this.title = title; 38 | this.body = body; 39 | this.picture = picture; 40 | } 41 | 42 | @Exclude 43 | public Map toMap() { 44 | HashMap result = new HashMap<>(); 45 | result.put("uid", uid); 46 | result.put("speaker", speaker); 47 | result.put("avatar", avatar); 48 | 49 | result.put("title", title); 50 | result.put("body", body); 51 | result.put("picture", picture); 52 | 53 | result.put("status", status); 54 | if (timeAdd == 0) { 55 | result.put("timeAdd", ServerValue.TIMESTAMP); 56 | } else { 57 | result.put("timeAdd", timeAdd); 58 | } 59 | 60 | result.put("voteCount", voteCount); 61 | result.put("joinCount", joinCount); 62 | result.put("redeemCount", redeemCount); 63 | 64 | result.put("votes", votes); 65 | result.put("joins", joins); 66 | return result; 67 | } 68 | } -------------------------------------------------------------------------------- /app/src/main/java/com/firebase/devday/models/User.java: -------------------------------------------------------------------------------- 1 | package com.firebase.devday.models; 2 | 3 | import com.google.firebase.database.IgnoreExtraProperties; 4 | 5 | @IgnoreExtraProperties 6 | public class User { 7 | public boolean isAdmin; 8 | public String email; 9 | public String avatar; 10 | public String speaker; 11 | 12 | public User() { 13 | // Default constructor required for calls to DataSnapshot.getValue(User.class) 14 | } 15 | 16 | public User(String email, String speaker, String avatar, boolean isAdmin) { 17 | this.email = email; 18 | this.speaker = speaker; 19 | this.avatar = avatar; 20 | this.isAdmin = isAdmin; 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jirawatee/FirebaseDevDay-Android/981e9e64649da6a9c014de955a48f15fb2f39b22/app/src/main/res/drawable-hdpi/ic_check.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jirawatee/FirebaseDevDay-Android/981e9e64649da6a9c014de955a48f15fb2f39b22/app/src/main/res/drawable-xhdpi/ic_check.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jirawatee/FirebaseDevDay-Android/981e9e64649da6a9c014de955a48f15fb2f39b22/app/src/main/res/drawable-xxhdpi/ic_check.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_check.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jirawatee/FirebaseDevDay-Android/981e9e64649da6a9c014de955a48f15fb2f39b22/app/src/main/res/drawable-xxxhdpi/ic_check.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_detail.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 17 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 |