diff --git a/.gitignore b/.gitignore index ddffdfe7f..8615e3386 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,7 @@ .gradle +/**/*.iml /local.properties .idea .DS_Store /build -/captures -/library/target -/**/*.iml google-services.json -build/ diff --git a/.travis.yml b/.travis.yml index df4e731ff..847010aee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,14 +9,27 @@ cache: directories: - $HOME/.gradle/caches/ - $HOME/.gradle/wrapper/ + - $HOME/.android/build-cache android: components: - - platform-tools - tools - - build-tools-25.0.1 + - build-tools-25.0.2 - android-25 - - # Extras - - extra-google-m2repository + - platform-tools - extra-android-m2repository -script: ./gradlew clean :library:testAll :library:prepareArtifacts + - extra-google-m2repository +before_script: mv library/google-services.json app/google-services.json +script: ./gradlew clean assembleDebug check +after_failure: + # tests + - cat app/build/reports/tests/testDebugUnitTest/index.html + - cat auth/build/reports/tests/testDebugUnitTest/index.html + - cat database/build/reports/tests/testDebugUnitTest/index.html + - cat storage/build/reports/tests/testDebugUnitTest/index.html + + # app + - cat app/build/reports/checkstyle.html + - cat app/build/reports/lint-results.xml + - cat app/build/reports/lint-results.html + - cat app/build/reports/findbugs.html + - cat app/build/reports/pmd.html diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6f5f2c969..8178aef42 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -30,8 +30,10 @@ Feature parity (where it makes sense) is a strict requirement for feature develo ### Code reviews All submissions, including submissions by project members, require review. We -use Github pull requests for this purpose. We adhere to the +use GitHub pull requests for this purpose. We adhere to the [Google Java style guide](https://google.github.io/styleguide/javaguide.html). +In addition, style and lint checks are run on each Travis build to ensure quality. To run the full +suite of tests, checks, lint, etc, use `./gradlew check` (this will ensure the Travis build passes). ### The small print diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index ef2ac817a..21b841784 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -1,5 +1,6 @@ Hey there! So you want to contribute to FirebaseUI? Before you file this pull request, follow these steps: * Read [the contribution guidelines](CONTRIBUTING.md). + * Run `./gradlew check` to ensure the Travis build passes. * If this has been discussed in an issue, make sure to mention the issue number here. If not, go file an issue about this to make sure this is a desirable change. * If this is a new feature please co-ordinate with someone on [FirebaseUI-iOS](https://github.com/firebase/firebaseui-ios) to make sure that we can implement this on both platforms and maintain feature parity. diff --git a/README.md b/README.md index 73956a2ea..cfaa0cc45 100644 --- a/README.md +++ b/README.md @@ -28,16 +28,16 @@ libraries. ```groovy dependencies { // FirebaseUI Database only - compile 'com.firebaseui:firebase-ui-database:1.0.1' + compile 'com.firebaseui:firebase-ui-database:1.1.0' // FirebaseUI Auth only - compile 'com.firebaseui:firebase-ui-auth:1.0.1' + compile 'com.firebaseui:firebase-ui-auth:1.1.0' // FirebaseUI Storage only - compile 'com.firebaseui:firebase-ui-storage:1.0.1' + compile 'com.firebaseui:firebase-ui-storage:1.1.0' // Single target that includes all FirebaseUI libraries above - compile 'com.firebaseui:firebase-ui:1.0.1' + compile 'com.firebaseui:firebase-ui:1.1.0' } ``` @@ -67,6 +67,7 @@ For convenience, here are some examples: | FirebaseUI Version | Firebase/Play Services Version | |--------------------|--------------------------------| +| 1.1.0 | 10.0.0 or 10.0.1 | | 1.0.1 | 10.0.0 or 10.0.1 | | 1.0.0 | 9.8.0 | | 0.6.2 | 9.8.0 | diff --git a/app/build.gradle b/app/build.gradle index f077dd862..69016eb71 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,6 +1,7 @@ apply plugin: 'com.android.application' apply plugin: 'com.neenbedankt.android-apt' -apply from: "../common/constants.gradle" +apply from: "../constants.gradle" +apply from: '../library/quality/quality.gradle' android { compileSdkVersion compileSdk @@ -42,7 +43,6 @@ dependencies { // The following dependencies are not required to use the Firebase UI library. // They are used to make some aspects of the demo app implementation simpler for // demonstrative purposes, and you may find them useful in your own apps; YMMV. - compile 'com.github.bumptech.glide:glide:3.7.0' compile 'pub.devrel:easypermissions:0.2.1' compile 'com.jakewharton:butterknife:8.4.0' apt 'com.jakewharton:butterknife-compiler:8.4.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 124bf4d2b..52b5e1fca 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,7 @@ @@ -9,9 +10,12 @@ + android:theme="@style/AppTheme" + android:supportsRtl="false" + tools:ignore="GoogleAppIndexingWarning"> diff --git a/app/src/main/java/com/firebase/uidemo/ChooserActivity.java b/app/src/main/java/com/firebase/uidemo/ChooserActivity.java index c2bb3e175..a547470f4 100644 --- a/app/src/main/java/com/firebase/uidemo/ChooserActivity.java +++ b/app/src/main/java/com/firebase/uidemo/ChooserActivity.java @@ -77,22 +77,21 @@ public void onItemClick(int position) { public static class MyArrayAdapter extends ArrayAdapter { private Context mContext; - private Class[] mClasses; - public MyArrayAdapter(Context context, int resource, Class[] objects) { + public MyArrayAdapter(Context context, int resource, Class... objects) { super(context, resource, objects); - mContext = context; - mClasses = objects; } @Override public View getView(int position, View convertView, ViewGroup parent) { - View view = convertView; + View view; if (convertView == null) { LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(LAYOUT_INFLATER_SERVICE); view = inflater.inflate(android.R.layout.simple_list_item_2, null); + } else { + view = convertView; } ((TextView) view.findViewById(android.R.id.text1)).setText(DESCRIPTION_NAMES[position]); diff --git a/app/src/main/java/com/firebase/uidemo/auth/AuthUiActivity.java b/app/src/main/java/com/firebase/uidemo/auth/AuthUiActivity.java index f1ddddf9d..665d73106 100644 --- a/app/src/main/java/com/firebase/uidemo/auth/AuthUiActivity.java +++ b/app/src/main/java/com/firebase/uidemo/auth/AuthUiActivity.java @@ -33,8 +33,9 @@ import com.firebase.ui.auth.AuthUI; import com.firebase.ui.auth.AuthUI.IdpConfig; +import com.firebase.ui.auth.ErrorCodes; import com.firebase.ui.auth.IdpResponse; -import com.firebase.ui.auth.ui.ResultCodes; +import com.firebase.ui.auth.ResultCodes; import com.firebase.uidemo.R; import com.google.android.gms.common.Scopes; import com.google.firebase.auth.FirebaseAuth; @@ -50,7 +51,7 @@ public class AuthUiActivity extends AppCompatActivity { private static final String UNCHANGED_CONFIG_VALUE = "CHANGE-ME"; private static final String GOOGLE_TOS_URL = "https://www.google.com/policies/terms/"; - private static final String FIREBASE_TOS_URL = "https://www.firebase.com/terms/terms-of-service.html"; + private static final String FIREBASE_TOS_URL = "https://firebase.google.com/terms/"; private static final int RC_SIGN_IN = 100; @BindView(R.id.default_theme) @@ -86,7 +87,7 @@ public class AuthUiActivity extends AppCompatActivity { @BindView(R.id.sign_in) Button mSignIn; - @BindView(android.R.id.content) + @BindView(R.id.root) View mRootView; @BindView(R.id.firebase_logo) @@ -199,20 +200,30 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { @MainThread private void handleSignInResponse(int resultCode, Intent data) { - if (resultCode == RESULT_OK) { - startActivity(SignedInActivity.createIntent(this, IdpResponse.fromResultIntent(data))); - finish(); - return; - } - - if (resultCode == RESULT_CANCELED) { - showSnackbar(R.string.sign_in_cancelled); - return; - } + IdpResponse response = IdpResponse.fromResultIntent(data); - if (resultCode == ResultCodes.RESULT_NO_NETWORK) { - showSnackbar(R.string.no_internet_connection); + // Successfully signed in + if (resultCode == ResultCodes.OK) { + startActivity(SignedInActivity.createIntent(this, response)); + finish(); return; + } else { + // Sign in failed + if (response == null) { + // User pressed back button + showSnackbar(R.string.sign_in_cancelled); + return; + } + + if (response.getErrorCode() == ErrorCodes.NO_NETWORK) { + showSnackbar(R.string.no_internet_connection); + return; + } + + if (response.getErrorCode() == ErrorCodes.UNKNOWN_ERROR) { + showSnackbar(R.string.unknown_error); + return; + } } showSnackbar(R.string.unknown_sign_in_response); @@ -269,6 +280,10 @@ private List getSelectedProviders() { selectedProviders.add(new IdpConfig.Builder(AuthUI.EMAIL_PROVIDER).build()); } + if (mUseTwitterProvider.isChecked()) { + selectedProviders.add(new IdpConfig.Builder(AuthUI.TWITTER_PROVIDER).build()); + } + if (mUseFacebookProvider.isChecked()) { selectedProviders.add( new IdpConfig.Builder(AuthUI.FACEBOOK_PROVIDER) @@ -283,10 +298,6 @@ private List getSelectedProviders() { .build()); } - if (mUseTwitterProvider.isChecked()) { - selectedProviders.add(new IdpConfig.Builder(AuthUI.TWITTER_PROVIDER).build()); - } - return selectedProviders; } @@ -302,20 +313,20 @@ private String getSelectedTosUrl() { @MainThread private boolean isGoogleConfigured() { return !UNCHANGED_CONFIG_VALUE.equals( - getResources().getString(R.string.default_web_client_id)); + getString(R.string.default_web_client_id)); } @MainThread private boolean isFacebookConfigured() { return !UNCHANGED_CONFIG_VALUE.equals( - getResources().getString(R.string.facebook_application_id)); + getString(R.string.facebook_application_id)); } @MainThread private boolean isTwitterConfigured() { List twitterConfigs = Arrays.asList( - getResources().getString(R.string.twitter_consumer_key), - getResources().getString(R.string.twitter_consumer_secret) + getString(R.string.twitter_consumer_key), + getString(R.string.twitter_consumer_secret) ); return !twitterConfigs.contains(UNCHANGED_CONFIG_VALUE); diff --git a/app/src/main/java/com/firebase/uidemo/auth/SignedInActivity.java b/app/src/main/java/com/firebase/uidemo/auth/SignedInActivity.java index d6f000ecb..4642ff034 100644 --- a/app/src/main/java/com/firebase/uidemo/auth/SignedInActivity.java +++ b/app/src/main/java/com/firebase/uidemo/auth/SignedInActivity.java @@ -28,9 +28,7 @@ import android.view.View; import android.widget.ImageView; import android.widget.TextView; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; + import com.bumptech.glide.Glide; import com.firebase.ui.auth.AuthUI; import com.firebase.ui.auth.IdpResponse; @@ -42,11 +40,14 @@ import com.google.firebase.auth.FirebaseAuth; import com.google.firebase.auth.FirebaseUser; import com.google.firebase.auth.GoogleAuthProvider; + import java.util.Iterator; -public class SignedInActivity extends AppCompatActivity { - private static final String EXTRA_IDP_RESPONSE = "extra_idp_response"; +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; +public class SignedInActivity extends AppCompatActivity { @BindView(android.R.id.content) View mRootView; @@ -62,6 +63,8 @@ public class SignedInActivity extends AppCompatActivity { @BindView(R.id.user_enabled_providers) TextView mEnabledProviders; + private IdpResponse mIdpResponse; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -73,6 +76,8 @@ public void onCreate(Bundle savedInstanceState) { return; } + mIdpResponse = IdpResponse.fromResultIntent(getIntent()); + setContentView(R.layout.signed_in_layout); ButterKnife.bind(this); populateProfile(); @@ -144,7 +149,7 @@ private void populateProfile() { mUserDisplayName.setText( TextUtils.isEmpty(user.getDisplayName()) ? "No display name" : user.getDisplayName()); - StringBuilder providerList = new StringBuilder(); + StringBuilder providerList = new StringBuilder(100); providerList.append("Providers used: "); @@ -174,20 +179,21 @@ private void populateProfile() { } private void populateIdpToken() { - IdpResponse idpResponse = getIntent().getParcelableExtra(EXTRA_IDP_RESPONSE); - if (idpResponse != null) { - String token = idpResponse.getIdpToken(); - String secret = idpResponse.getIdpSecret(); - if (token == null) { - findViewById(R.id.idp_token_layout).setVisibility(View.GONE); - } else { - ((TextView) findViewById(R.id.idp_token)).setText(token); - } - if (secret == null) { - findViewById(R.id.idp_secret_layout).setVisibility(View.GONE); - } else { - ((TextView) findViewById(R.id.idp_secret)).setText(secret); - } + String token = null; + String secret = null; + if (mIdpResponse != null) { + token = mIdpResponse.getIdpToken(); + secret = mIdpResponse.getIdpSecret(); + } + if (token == null) { + findViewById(R.id.idp_token_layout).setVisibility(View.GONE); + } else { + ((TextView) findViewById(R.id.idp_token)).setText(token); + } + if (secret == null) { + findViewById(R.id.idp_secret_layout).setVisibility(View.GONE); + } else { + ((TextView) findViewById(R.id.idp_secret)).setText(secret); } } @@ -198,9 +204,8 @@ private void showSnackbar(@StringRes int errorMessageRes) { } public static Intent createIntent(Context context, IdpResponse idpResponse) { - Intent in = new Intent(); + Intent in = IdpResponse.getIntent(idpResponse); in.setClass(context, SignedInActivity.class); - in.putExtra(EXTRA_IDP_RESPONSE, idpResponse); return in; } } diff --git a/app/src/main/java/com/firebase/uidemo/database/ChatActivity.java b/app/src/main/java/com/firebase/uidemo/database/ChatActivity.java index 9745a7e8a..3eaf2fc25 100644 --- a/app/src/main/java/com/firebase/uidemo/database/ChatActivity.java +++ b/app/src/main/java/com/firebase/uidemo/database/ChatActivity.java @@ -46,9 +46,9 @@ import com.google.firebase.database.FirebaseDatabase; import com.google.firebase.database.Query; +@SuppressWarnings("LogConditional") public class ChatActivity extends AppCompatActivity implements FirebaseAuth.AuthStateListener { - - public static final String TAG = "RecyclerViewDemo"; + private static final String TAG = "RecyclerViewDemo"; private FirebaseAuth mAuth; private DatabaseReference mRef; @@ -59,6 +59,7 @@ public class ChatActivity extends AppCompatActivity implements FirebaseAuth.Auth private RecyclerView mMessages; private LinearLayoutManager mManager; private FirebaseRecyclerAdapter mRecyclerViewAdapter; + private View mEmptyListView; @Override protected void onCreate(Bundle savedInstanceState) { @@ -71,6 +72,8 @@ protected void onCreate(Bundle savedInstanceState) { mSendButton = (Button) findViewById(R.id.sendButton); mMessageEdit = (EditText) findViewById(R.id.messageEdit); + mEmptyListView = findViewById(R.id.emptyTextView); + mRef = FirebaseDatabase.getInstance().getReference(); mChatRef = mRef.child("chats"); @@ -80,7 +83,7 @@ public void onClick(View v) { String uid = mAuth.getCurrentUser().getUid(); String name = "User " + uid.substring(0, 6); - Chat chat = new Chat(name, uid, mMessageEdit.getText().toString()); + Chat chat = new Chat(name, mMessageEdit.getText().toString(), uid); mChatRef.push().setValue(chat, new DatabaseReference.CompletionListener() { @Override public void onComplete(DatabaseError databaseError, DatabaseReference reference) { @@ -146,7 +149,7 @@ private void attachRecyclerViewAdapter() { @Override public void populateViewHolder(ChatHolder chatView, Chat chat, int position) { chatView.setName(chat.getName()); - chatView.setText(chat.getText()); + chatView.setText(chat.getMessage()); FirebaseUser currentUser = mAuth.getCurrentUser(); if (currentUser != null && chat.getUid().equals(currentUser.getUid())) { @@ -155,6 +158,12 @@ public void populateViewHolder(ChatHolder chatView, Chat chat, int position) { chatView.setIsSender(false); } } + + @Override + protected void onDataChanged() { + // if there are no chat messages, show a view that invites the user to add a message + mEmptyListView.setVisibility(mRecyclerViewAdapter.getItemCount() == 0 ? View.VISIBLE : View.INVISIBLE); + } }; // Scroll to bottom on new messages @@ -188,7 +197,7 @@ public void onComplete(@NonNull Task task) { } public boolean isSignedIn() { - return (mAuth.getCurrentUser() != null); + return mAuth.getCurrentUser() != null; } public void updateUI() { @@ -198,30 +207,42 @@ public void updateUI() { } public static class Chat { - - String name; - String text; - String uid; + private String mName; + private String mMessage; + private String mUid; public Chat() { + // Needed for Firebase } - public Chat(String name, String uid, String message) { - this.name = name; - this.text = message; - this.uid = uid; + public Chat(String name, String message, String uid) { + mName = name; + mMessage = message; + mUid = uid; } public String getName() { - return name; + return mName; + } + + public void setName(String name) { + mName = name; + } + + public String getMessage() { + return mMessage; + } + + public void setMessage(String message) { + mMessage = message; } public String getUid() { - return uid; + return mUid; } - public String getText() { - return text; + public void setUid(String uid) { + mUid = uid; } } diff --git a/app/src/main/java/com/firebase/uidemo/storage/ImageActivity.java b/app/src/main/java/com/firebase/uidemo/storage/ImageActivity.java index 4d1471af8..997516a71 100644 --- a/app/src/main/java/com/firebase/uidemo/storage/ImageActivity.java +++ b/app/src/main/java/com/firebase/uidemo/storage/ImageActivity.java @@ -34,6 +34,7 @@ import pub.devrel.easypermissions.AfterPermissionGranted; import pub.devrel.easypermissions.EasyPermissions; +@SuppressWarnings("LogConditional") public class ImageActivity extends AppCompatActivity { private static final String TAG = "ImageDemo"; diff --git a/app/src/main/res/drawable/chat_message_arrow.xml b/app/src/main/res/drawable/chat_message_arrow.xml index 5ee79fae9..a9f1b5f9b 100644 --- a/app/src/main/res/drawable/chat_message_arrow.xml +++ b/app/src/main/res/drawable/chat_message_arrow.xml @@ -10,6 +10,6 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/chat_message_background.xml b/app/src/main/res/drawable/chat_message_background.xml index bac849549..ebef62f6c 100644 --- a/app/src/main/res/drawable/chat_message_background.xml +++ b/app/src/main/res/drawable/chat_message_background.xml @@ -3,5 +3,5 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> - + diff --git a/app/src/main/res/layout/activity_chat.xml b/app/src/main/res/layout/activity_chat.xml index 98639fc10..1c9c4d8a1 100644 --- a/app/src/main/res/layout/activity_chat.xml +++ b/app/src/main/res/layout/activity_chat.xml @@ -6,6 +6,13 @@ android:layout_height="match_parent" tools:context=".database.ChatActivity"> + + + android:paddingLeft="16dp" + android:paddingStart="16dp"> + android:layout_weight="1" + android:inputType="text"/>