diff --git a/.gitignore b/.gitignore index dd492fd47..ddffdfe7f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,4 @@ .gradle -gradle.properties /local.properties .idea .DS_Store diff --git a/README.md b/README.md index 20fe36c07..a15282f48 100644 --- a/README.md +++ b/README.md @@ -28,13 +28,16 @@ libraries. ```groovy dependencies { // Single target that includes all FirebaseUI libraries - compile 'com.firebaseui:firebase-ui:0.5.3' + compile 'com.firebaseui:firebase-ui:0.6.0' // FirebaseUI Database only - compile 'com.firebaseui:firebase-ui-database:0.5.3' + compile 'com.firebaseui:firebase-ui-database:0.6.0' // FirebaseUI Auth only - compile 'com.firebaseui:firebase-ui-auth:0.5.3' + compile 'com.firebaseui:firebase-ui-auth:0.6.0' + + // FirebaseUI Storage only + compile 'com.firebaseui:firebase-ui-storage:0.6.0' } ``` @@ -50,6 +53,9 @@ firebase-ui-auth firebase-ui-database |--- com.google.firebase:firebase-database + +firebase-ui-storage +|--- com.google.firebase:firebase-storage ``` Each version of FirebaseUI has dependency on a fixed version of these libraries, defined as the variable `firebase_version` @@ -61,6 +67,7 @@ For convenience, here are some examples: | FirebaseUI Version | Firebase/Play Services Version | |--------------------|--------------------------------| +| 0.6.0 | 9.6.0 | | 0.5.3 | 9.4.0 | | 0.4.4 | 9.4.0 | | 0.4.3 | 9.2.1 | @@ -72,6 +79,7 @@ For convenience, here are some examples: * [firebase-ui-database](database/README.md) * [firebase-ui-auth](auth/README.md) + * [firebase-ui-storage](storage/README.md) ## Sample App diff --git a/app/build.gradle b/app/build.gradle index c5bd1da83..28e3b2b46 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,8 +16,12 @@ android { buildTypes { release { - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + minifyEnabled true + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + + // For the purposes of the sample, allow testing of a proguarded release build + // using the debug key + signingConfig signingConfigs.debug } } @@ -32,9 +36,11 @@ dependencies { compile project(path: ':auth') compile project(path: ':database') + compile project(path: ':storage') compile "com.google.firebase:firebase-database:${project.ext.firebase_version}" compile "com.google.firebase:firebase-auth:${project.ext.firebase_version}" + compile "com.google.firebase:firebase-storage:${project.ext.firebase_version}" compile "com.google.android.gms:play-services-auth:${project.ext.firebase_version}" // The following dependencies are not required to use the Firebase UI library. @@ -42,6 +48,7 @@ dependencies { // demonstrative purposes, and you may find them useful in your own apps; YMMV. compile 'com.jakewharton:butterknife:8.0.1' compile 'com.github.bumptech.glide:glide:3.7.0' + compile 'pub.devrel:easypermissions:0.2.0' apt 'com.jakewharton:butterknife-compiler:8.0.1' } diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 6246f9b98..b7e38932b 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -20,3 +20,11 @@ # https://firebase-dot-devsite.googleplex.com/docs/auth/android/start/#proguard -keepattributes Signature -keepattributes *Annotation* + +# See: +# storage/README.md +-assumenosideeffects class android.util.Log { + public static *** w(...); + public static *** d(...); + public static *** v(...); +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 48b59198c..48ce0ba09 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,13 +3,13 @@ package="com.firebase.uidemo"> + - @@ -27,10 +27,10 @@ - + diff --git a/app/src/main/java/com/firebase/uidemo/ChooserActivity.java b/app/src/main/java/com/firebase/uidemo/ChooserActivity.java index ee7a1f677..c2bb3e175 100644 --- a/app/src/main/java/com/firebase/uidemo/ChooserActivity.java +++ b/app/src/main/java/com/firebase/uidemo/ChooserActivity.java @@ -27,6 +27,7 @@ import com.firebase.uidemo.auth.AuthUiActivity; import com.firebase.uidemo.database.ChatActivity; +import com.firebase.uidemo.storage.ImageActivity; import butterknife.BindView; import butterknife.ButterKnife; @@ -37,16 +38,19 @@ public class ChooserActivity extends AppCompatActivity { private static final Class[] CLASSES = new Class[]{ ChatActivity.class, AuthUiActivity.class, + ImageActivity.class, }; private static final int[] DESCRIPTION_NAMES = new int[] { R.string.name_chat, - R.string.name_auth_ui + R.string.name_auth_ui, + R.string.name_image }; private static final int[] DESCRIPTION_IDS = new int[] { R.string.desc_chat, - R.string.desc_auth_ui + R.string.desc_auth_ui, + R.string.desc_image }; @BindView(R.id.list_view) diff --git a/app/src/main/java/com/firebase/uidemo/storage/ImageActivity.java b/app/src/main/java/com/firebase/uidemo/storage/ImageActivity.java new file mode 100644 index 000000000..4d1471af8 --- /dev/null +++ b/app/src/main/java/com/firebase/uidemo/storage/ImageActivity.java @@ -0,0 +1,167 @@ +package com.firebase.uidemo.storage; + +import android.Manifest; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.provider.MediaStore; +import android.support.annotation.NonNull; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.Toast; + +import com.bumptech.glide.Glide; +import com.firebase.ui.storage.images.FirebaseImageLoader; +import com.firebase.uidemo.R; +import com.google.android.gms.tasks.OnCompleteListener; +import com.google.android.gms.tasks.OnFailureListener; +import com.google.android.gms.tasks.OnSuccessListener; +import com.google.android.gms.tasks.Task; +import com.google.firebase.auth.AuthResult; +import com.google.firebase.auth.FirebaseAuth; +import com.google.firebase.storage.FirebaseStorage; +import com.google.firebase.storage.StorageReference; +import com.google.firebase.storage.UploadTask; + +import java.util.UUID; + +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; +import pub.devrel.easypermissions.AfterPermissionGranted; +import pub.devrel.easypermissions.EasyPermissions; + +public class ImageActivity extends AppCompatActivity { + + private static final String TAG = "ImageDemo"; + private static final int RC_CHOOSE_PHOTO = 101; + private static final int RC_IMAGE_PERMS = 102; + + private StorageReference mImageRef; + + @BindView(R.id.button_choose_photo) + Button mUploadButton; + + @BindView(R.id.button_download_direct) + Button mDownloadDirectButton; + + @BindView(R.id.first_image) + ImageView mImageView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_image); + ButterKnife.bind(this); + + // By default, Firebase Storage files require authentication to read or write. + // For this sample to function correctly, enable Anonymous Auth in the Firebase console: + // https://console.firebase.google.com/project/_/authentication/providers + FirebaseAuth.getInstance().signInAnonymously() + .addOnCompleteListener(new OnCompleteListener() { + @Override + public void onComplete(@NonNull Task task) { + Log.d(TAG, "signInAnonymously:" + task.isSuccessful()); + if (!task.isSuccessful()) { + Log.w(TAG, "signInAnonymously", task.getException()); + Log.w(TAG, getString(R.string.anonymous_auth_failed_msg)); + + Toast.makeText(ImageActivity.this, + getString(R.string.anonymous_auth_failed_toast), + Toast.LENGTH_SHORT).show(); + } + } + }); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + if (requestCode == RC_CHOOSE_PHOTO) { + if (resultCode == RESULT_OK) { + Uri selectedImage = data.getData(); + uploadPhoto(selectedImage); + } else { + Toast.makeText(this, "No image chosen", Toast.LENGTH_SHORT).show(); + } + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this); + } + + @OnClick(R.id.button_choose_photo) + @AfterPermissionGranted(RC_IMAGE_PERMS) + protected void choosePhoto() { + String perm = Manifest.permission.READ_EXTERNAL_STORAGE; + if (!EasyPermissions.hasPermissions(this, perm)) { + EasyPermissions.requestPermissions(this, getString(R.string.rational_image_perm), + RC_IMAGE_PERMS, perm); + return; + } + + Intent i = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); + startActivityForResult(i, RC_CHOOSE_PHOTO); + } + + protected void uploadPhoto(Uri uri) { + // Reset UI + hideDownloadUI(); + Toast.makeText(this, "Uploading...", Toast.LENGTH_SHORT).show(); + + // Upload to Firebase Storage + String uuid = UUID.randomUUID().toString(); + mImageRef = FirebaseStorage.getInstance().getReference(uuid); + mImageRef.putFile(uri) + .addOnSuccessListener(this, new OnSuccessListener() { + @Override + public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { + Log.d(TAG, "uploadPhoto:onSuccess:" + + taskSnapshot.getMetadata().getReference().getPath()); + Toast.makeText(ImageActivity.this, "Image uploaded", + Toast.LENGTH_SHORT).show(); + + showDownloadUI(); + } + }) + .addOnFailureListener(this, new OnFailureListener() { + @Override + public void onFailure(@NonNull Exception e) { + Log.w(TAG, "uploadPhoto:onError", e); + Toast.makeText(ImageActivity.this, "Upload failed", + Toast.LENGTH_SHORT).show(); + } + }); + } + + @OnClick(R.id.button_download_direct) + protected void downloadDirect() { + // Download directly from StorageReference using Glide + Glide.with(this) + .using(new FirebaseImageLoader()) + .load(mImageRef) + .centerCrop() + .crossFade() + .into(mImageView); + } + + private void hideDownloadUI() { + mDownloadDirectButton.setEnabled(false); + + mImageView.setImageResource(0); + mImageView.setVisibility(View.INVISIBLE); + } + + private void showDownloadUI() { + mDownloadDirectButton.setEnabled(true); + + mImageView.setVisibility(View.VISIBLE); + } +} diff --git a/app/src/main/res/layout/activity_image.xml b/app/src/main/res/layout/activity_image.xml new file mode 100644 index 000000000..ca4bebbe3 --- /dev/null +++ b/app/src/main/res/layout/activity_image.xml @@ -0,0 +1,56 @@ + + + + + +