diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..de3ba2f5b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,370 @@ +# Contributing to FirebaseUI-Android + + + Android CI GitHub Workflow Status + + +_See also: [Firebase's code of conduct](https://firebase.google.com/support/guides/code-conduct)_ + +## 1. Things you will need + +- Linux, Mac OS X, or Windows. +- [git](https://git-scm.com) (used for source version control). +- An ssh client (used to authenticate with GitHub). +- [Android Studio](https://developer.android.com/studio) or [IntelliJ IDEA](https://www.jetbrains.com/idea/). +- [JDK 21](https://adoptium.net/) or higher. +- [Android SDK](https://developer.android.com/studio) with minimum API level 21. + +## 2. Forking & cloning the repository + +- Ensure all the dependencies described in the previous section are installed. +- Fork `https://github.com/firebase/FirebaseUI-Android` into your own GitHub account. If + you already have a fork, and are now installing a development environment on + a new machine, make sure you've updated your fork so that you don't use stale + configuration options from long ago. +- If you haven't configured your machine with an SSH key that's known to github, then + follow [GitHub's directions](https://help.github.com/articles/generating-ssh-keys/) + to generate an SSH key. +- `git clone git@github.com:/FirebaseUI-Android.git` +- `git remote add upstream git@github.com:firebase/FirebaseUI-Android.git` (So that you + fetch from the main repository, not your clone, when running `git fetch` + et al.) + +## 3. Environment Setup + +FirebaseUI-Android uses Gradle to manage the project and dependencies. + +The repository includes a `library/google-services.json` file that is copied to the app and test modules during builds. This is handled automatically by the build scripts. + +To verify your environment is set up correctly, run: + +```bash +./scripts/build.sh +``` + +This will: +- Copy the necessary `google-services.json` files +- Download all dependencies +- Build all modules +- Run checkstyle +- Run unit tests + +> If you need to use your own Firebase project, replace `library/google-services.json` with your own configuration file from the [Firebase Console](https://console.firebase.google.com/). + +## 4. Running an example + +The project provides a demo app in the `app` module which showcases the main use-cases of FirebaseUI Auth. + +To run the example app: + +**Option 1: Android Studio** +- Open the project in Android Studio +- Select the `app` configuration +- Run on a device or emulator + +**Option 2: Command line** +```bash +./gradlew :app:installDebug +``` + +Then launch the app on your device. + +Any changes made to the library modules (auth, database, firestore, storage) locally will be automatically reflected in the example application. + +## 5. Running tests + +FirebaseUI-Android comprises of a number of tests, including unit tests and E2E tests. + +### Unit tests + +Unit tests are responsible for ensuring expected behavior whilst developing the library's Kotlin/Java code. Unit tests do not +interact with 3rd party Firebase services, and mock where possible. To run unit tests for all modules (excluding e2eTest), run the following command from the root directory: + +```bash +./gradlew testDebugUnitTest -x :e2eTest:testDebugUnitTest +``` + +To run unit tests for a specific module (e.g., auth): + +```bash +./gradlew :auth:testDebugUnitTest +``` + +### E2E tests + +E2E tests run against Firebase Auth Emulator and test the full integration with Firebase services. To run e2e tests, you first need to start the Firebase Emulator: + +```bash +# Install Firebase Tools (if not already installed) +npm install -g firebase-tools + +# Start the Firebase Auth Emulator +./scripts/start-firebase-emulator.sh +``` + +Then in a separate terminal, run the e2e tests: + +```bash +./gradlew e2eTest +``` + +> Note: E2E tests use Firebase Emulator Suite, so you don't need a real Firebase project to run them. + +### Lint and Code Analysis + +To run lint checks: + +```bash +./gradlew checkstyle +``` + +## 6. Contributing code + +We gladly accept contributions via GitHub pull requests. + +Please peruse the +[Kotlin coding conventions](https://kotlinlang.org/docs/coding-conventions.html) and +[Android code style guide](https://developer.android.com/kotlin/style-guide) before +working on anything non-trivial. These guidelines are intended to +keep the code consistent and avoid common pitfalls. + +To start working on a patch: + +1. `git fetch upstream` +2. `git checkout upstream/master -b ` +3. Hack away! + +Once you have made your changes, ensure that it passes the internal analyzer & formatting checks. The following +commands can be run locally to highlight any issues before committing your code: + +```bash +# Run the full CI build script +./scripts/build.sh +``` + +This script runs: +- `./gradlew clean` +- `./gradlew assembleDebug` - Build all modules +- `./gradlew checkstyle` - Run code style checks +- `./gradlew testDebugUnitTest -x :e2eTest:testDebugUnitTest` - Run unit tests + +You can also run these commands individually if needed. + +Assuming all is successful, commit and push your code: + +1. `git commit -a -m ""` +2. `git push origin ` + +To send us a pull request: + +- `git pull-request` (if you are using [Hub](http://github.com/github/hub/)) or + go to `https://github.com/firebase/FirebaseUI-Android` and click the + "Compare & pull request" button + +Please make sure all your check-ins have detailed commit messages explaining the patch. + +When naming the title of your pull request, please follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) +guide. For example, for a fix to the FirebaseUI Auth module: + +`fix(auth): fixed a bug with email sign-in!` + +For a new feature: + +`feat(auth): add support for passkey authentication` + +Tests are run automatically on contributions using GitHub Actions. Depending on +your code contributions, various tests will be run against your updated code automatically. + +Once you've gotten an LGTM from a project maintainer and once your PR has received +the green light from all our automated testing, wait for one of the package maintainers +to merge the pull request. + +### Code style + +FirebaseUI-Android follows standard Kotlin and Android conventions: + +#### Kotlin + +- Use 4 spaces for indentation +- Maximum line length: 120 characters +- Use meaningful variable and function names +- Follow [Kotlin coding conventions](https://kotlinlang.org/docs/coding-conventions.html) + +#### Jetpack Compose + +- Follow [Compose API guidelines](https://github.com/androidx/androidx/blob/androidx-main/compose/docs/compose-api-guidelines.md) +- Composables should be stateless when possible +- Use `remember` for state that survives recomposition +- Hoist state when it needs to be shared + +#### Example + +```kotlin +@Composable +fun SignInScreen( + configuration: AuthUIConfiguration, + onSignInSuccess: (AuthResult) -> Unit, + modifier: Modifier = Modifier +) { + var email by remember { mutableStateOf("") } + var password by remember { mutableStateOf("") } + + Column( + modifier = modifier + .fillMaxSize() + .padding(16.dp) + ) { + OutlinedTextField( + value = email, + onValueChange = { email = it }, + label = { Text("Email") }, + modifier = Modifier.fillMaxWidth() + ) + + Spacer(modifier = Modifier.height(8.dp)) + + OutlinedTextField( + value = password, + onValueChange = { password = it }, + label = { Text("Password") }, + visualTransformation = PasswordVisualTransformation(), + modifier = Modifier.fillMaxWidth() + ) + } +} +``` + +### Documentation + +All public APIs should be documented with KDoc: + +```kotlin +/** + * Authenticates a user with email and password. + * + * @param email The user's email address + * @param password The user's password + * @return [AuthResult] containing the signed-in user + * @throws AuthException.InvalidCredentialsException if credentials are invalid + * @throws AuthException.NetworkException if network is unavailable + * + * Example usage: + * ```kotlin + * val result = authUI.signInWithEmailAndPassword( + * email = "user@example.com", + * password = "securePassword123" + * ) + * ``` + */ +suspend fun signInWithEmailAndPassword( + email: String, + password: String +): AuthResult +``` + +### Contributor License Agreement + +You must complete the +[Contributor License Agreement](https://cla.developers.google.com/clas). +You can do this online, and it only takes a minute. +If you've never submitted code before, you must add your (or your +organization's) name and contact info to the [AUTHORS](AUTHORS) file. + +### License Headers + +If you create a new file, do not forget to add the license header: + +```kotlin +/* + * Copyright 2025 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing permissions and + * limitations under the License. + */ +``` + +### The review process + +Newly opened PRs first go through initial triage which results in one of: + +- **Merging the PR** - if the PR can be quickly reviewed and looks good. +- **Closing the PR** - if the PR maintainer decides that the PR should not be merged. +- **Moving the PR to the backlog** - if the review requires non-trivial effort and the issue isn't a priority; in this case the maintainer will: + - Make sure that the PR has an associated issue labeled with "auth", "database", "firestore", or "storage". + - Add the "backlog" label to the issue. + - Leave a comment on the PR explaining that the review is not trivial and that the issue will be looked at according to priority order. +- **Starting a non-trivial review** - if the review requires non-trivial effort and the issue is a priority; in this case the maintainer will: + - Add the "in review" label to the issue. + - Self assign the PR. +- **API Changes** + - If a change or improvement will affect public API, the team will take longer in the review process. + +### The release process + +We push releases manually, using Gradle and Maven publishing. + +Changelogs and version updates are managed by project maintainers. The new version is automatically +generated via the commit types and changelogs via the commit messages. + +Some things to keep in mind before publishing the release: + +- Has CI run on the main commit and gone green? Even if CI shows as green on + the PR it's still possible for it to fail on merge, for multiple reasons. + There may have been some bug in the merge that introduced new failures. CI + runs on PRs as it's configured on their branch state, and not on tip of tree. +- [Publishing is + forever.](https://central.sonatype.org/publish/publish-guide/#deployment) + Hopefully any bugs or breaking changes in this PR have already been caught + in PR review, but now's a second chance to revert before anything goes live. +- "Don't deploy on a Friday." Consider carefully whether or not it's worth + immediately publishing an update before a stretch of time where you're going + to be unavailable. There may be bugs with the release or questions about it + from people that immediately adopt it, and uncovering and resolving those + support issues will take more time if you're unavailable. + +## 7. Contributing documentation + +We gladly accept contributions to the SDK documentation. As our docs are also part of this repo, +see "Contributing code" above for how to prepare and submit a PR to the repo. + +FirebaseUI-Android documentation lives in the README files for each module: +- `auth/README.md` - FirebaseUI Auth documentation +- `database/README.md` - FirebaseUI Realtime Database documentation +- `firestore/README.md` - FirebaseUI Firestore documentation +- `storage/README.md` - FirebaseUI Storage documentation + +Firebase follows the [Google developer documentation style guide](https://developers.google.com/style), +which you should read before writing substantial contributions. + +When updating documentation: + +1. Ensure code samples are tested and working +2. Follow Markdown best practices +3. Include screenshots or GIFs for UI-related changes +4. Update table of contents if adding new sections +5. Ensure links are valid and point to the correct locations + +## 8. Getting help + +If you have questions about contributing: + +- Check the module README files +- Check existing [issues](https://github.com/firebase/FirebaseUI-Android/issues) and [pull requests](https://github.com/firebase/FirebaseUI-Android/pulls) +- Ask on [Stack Overflow](https://stackoverflow.com/questions/tagged/firebaseui) with the `firebaseui` tag +- Create a new issue for discussion + +## 9. Recognition + +Contributors will be recognized in: +- Release notes +- GitHub contributors page +- Project AUTHORS file + +Thank you for making FirebaseUI-Android better! 🎉 diff --git a/composeapp/.gitignore b/app/.gitignore similarity index 100% rename from composeapp/.gitignore rename to app/.gitignore diff --git a/app/build.gradle.kts b/app/build.gradle.kts index cec75d966..70237e2d3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,102 +1,72 @@ -// NOTE: this project uses Gradle Kotlin DSL. More common build.gradle instructions can be found in -// the main README. plugins { - id("com.android.application") + id("com.android.application") + id("org.jetbrains.kotlin.android") + id("org.jetbrains.kotlin.plugin.compose") + id("com.google.gms.google-services") } android { - compileSdk = Config.SdkVersions.compile - namespace = "com.firebase.uidemo" + compileSdk = Config.SdkVersions.compile defaultConfig { + applicationId = "com.firebase.uidemo" minSdk = Config.SdkVersions.min targetSdk = Config.SdkVersions.target - - versionName = Config.version versionCode = 1 + versionName = "1.0" - resourcePrefix("fui_") - vectorDrawables.useSupportLibrary = true - } - - defaultConfig { - multiDexEnabled = true + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } buildTypes { - named("release").configure { - // For the purposes of the sample, allow testing of a proguarded release build - // using the debug key - signingConfig = signingConfigs["debug"] - - postprocessing { - isRemoveUnusedCode = true - isRemoveUnusedResources = true - isObfuscate = true - isOptimizeCode = true + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + // Only sign with debug keystore if it exists (for local testing) + val debugKeystoreFile = file("${System.getProperty("user.home")}/.android/debug.keystore") + if (debugKeystoreFile.exists()) { + signingConfig = signingConfigs.getByName("debug") } } } - - lint { - // Common lint options across all modules - - disable += mutableSetOf( - "IconExpectedSize", - "InvalidPackage", // Firestore uses GRPC which makes lint mad - "NewerVersionAvailable", "GradleDependency", // For reproducible builds - "SelectableText", "SyntheticAccessor", // We almost never care about this - "UnusedIds", "MediaCapabilities" // TODO(rosariopfernandes): remove this once we confirm - // it builds successfully - ) - - // Module-specific - disable += mutableSetOf("ResourceName", "MissingTranslation", "DuplicateStrings") - - checkAllWarnings = true - warningsAsErrors = true - abortOnError = true - - baseline = file("$rootDir/library/quality/lint-baseline.xml") - } - compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } - + kotlinOptions { + jvmTarget = "17" + } buildFeatures { - viewBinding = true + compose = true } } dependencies { - implementation(Config.Libs.Androidx.materialDesign) - implementation(Config.Libs.Androidx.multidex) - implementation(project(":auth")) - implementation(project(":firestore")) - implementation(project(":database")) - implementation(project(":storage")) + implementation(Config.Libs.Kotlin.jvm) + implementation(Config.Libs.Androidx.lifecycleRuntime) + implementation(Config.Libs.Androidx.Compose.activityCompose) + implementation(platform(Config.Libs.Androidx.Compose.bom)) + implementation(Config.Libs.Androidx.Compose.ui) + implementation(Config.Libs.Androidx.Compose.uiGraphics) + implementation(Config.Libs.Androidx.Compose.toolingPreview) + implementation(Config.Libs.Androidx.Compose.material3) + + + // Facebook implementation(Config.Libs.Provider.facebook) - // Needed to override Facebook - implementation(Config.Libs.Androidx.cardView) - implementation(Config.Libs.Androidx.customTabs) - implementation(Config.Libs.Misc.glide) - annotationProcessor(Config.Libs.Misc.glideCompiler) + testImplementation(Config.Libs.Test.junit) + androidTestImplementation(Config.Libs.Test.junitExt) + androidTestImplementation(platform(Config.Libs.Androidx.Compose.bom)) + androidTestImplementation(Config.Libs.Test.composeUiTestJunit4) - // Used for FirestorePagingActivity - implementation(Config.Libs.Androidx.paging) + debugImplementation(Config.Libs.Androidx.Compose.tooling) - // 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. - implementation(Config.Libs.Misc.permissions) - implementation(Config.Libs.Androidx.constraint) - debugImplementation(Config.Libs.Misc.leakCanary) + implementation(platform(Config.Libs.Firebase.bom)) } - -apply(plugin = "com.google.gms.google-services") diff --git a/composeapp/proguard-rules.pro b/app/proguard-rules.pro similarity index 100% rename from composeapp/proguard-rules.pro rename to app/proguard-rules.pro diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 60ccc6600..2477a1a0d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,77 +1,60 @@ - + - - + - - + - + + + + + + + + + + - - - - - - + android:name=".HighLevelApiDemoActivity" + android:label="High-Level API Demo" + android:exported="false" + android:theme="@style/Theme.FirebaseUIAndroid" /> - + android:name=".AuthFlowControllerDemoActivity" + android:label="Low-Level API Demo" + android:exported="false" + android:theme="@style/Theme.FirebaseUIAndroid" /> - - - - - - - - - - + android:name=".CustomSlotsThemingDemoActivity" + android:label="Custom Slots & Theming Demo" + android:exported="false" + android:theme="@style/Theme.FirebaseUIAndroid" /> - \ No newline at end of file + diff --git a/composeapp/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png similarity index 100% rename from composeapp/src/main/ic_launcher-playstore.png rename to app/src/main/ic_launcher-playstore.png diff --git a/composeapp/src/main/java/com/firebase/composeapp/AuthFlowControllerDemoActivity.kt b/app/src/main/java/com/firebase/uidemo/AuthFlowControllerDemoActivity.kt similarity index 99% rename from composeapp/src/main/java/com/firebase/composeapp/AuthFlowControllerDemoActivity.kt rename to app/src/main/java/com/firebase/uidemo/AuthFlowControllerDemoActivity.kt index dfdcb0cab..efca8c65e 100644 --- a/composeapp/src/main/java/com/firebase/composeapp/AuthFlowControllerDemoActivity.kt +++ b/app/src/main/java/com/firebase/uidemo/AuthFlowControllerDemoActivity.kt @@ -1,4 +1,4 @@ -package com.firebase.composeapp +package com.firebase.uidemo import android.app.Activity import android.content.Context @@ -86,7 +86,7 @@ class AuthFlowControllerDemoActivity : ComponentActivity() { url = "https://temp-test-aa342.firebaseapp.com" handleCodeInApp = true setAndroidPackageName( - "com.firebase.composeapp", + "com.firebase.uidemo", true, null ) diff --git a/app/src/main/java/com/firebase/uidemo/ChooserActivity.java b/app/src/main/java/com/firebase/uidemo/ChooserActivity.java deleted file mode 100644 index 4f7504299..000000000 --- a/app/src/main/java/com/firebase/uidemo/ChooserActivity.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2016 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.firebase.uidemo; - -import android.content.Intent; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; - -import com.firebase.ui.auth.AuthUI; -import com.firebase.ui.auth.util.ExtraConstants; -import com.firebase.uidemo.auth.AnonymousUpgradeActivity; -import com.firebase.uidemo.auth.AuthUiActivity; -import com.firebase.uidemo.database.firestore.FirestoreChatActivity; -import com.firebase.uidemo.database.firestore.FirestorePagingActivity; -import com.firebase.uidemo.database.realtime.FirebaseDbPagingActivity; -import com.firebase.uidemo.database.realtime.RealtimeDbChatActivity; -import com.firebase.uidemo.databinding.ActivityChooserBinding; -import com.firebase.uidemo.storage.ImageActivity; - -import androidx.annotation.Nullable; -import androidx.annotation.StringRes; -import androidx.appcompat.app.AppCompatActivity; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -public class ChooserActivity extends AppCompatActivity { - private ActivityChooserBinding mBinding; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - if (AuthUI.canHandleIntent(getIntent())) { - Intent intent = new Intent(ChooserActivity.this, AuthUiActivity - .class); - intent.putExtra(ExtraConstants.EMAIL_LINK_SIGN_IN, getIntent().getData().toString()); - startActivity(intent); - finish(); - return; - } - mBinding = ActivityChooserBinding.inflate(getLayoutInflater()); - setContentView(mBinding.getRoot()); - - mBinding.activities.setLayoutManager(new LinearLayoutManager(this)); - mBinding.activities.setAdapter(new ActivityChooserAdapter()); - mBinding.activities.setHasFixedSize(true); - } - - private static class ActivityChooserAdapter - extends RecyclerView.Adapter { - private static final Class[] CLASSES = new Class[]{ - AuthUiActivity.class, - AnonymousUpgradeActivity.class, - FirestoreChatActivity.class, - FirestorePagingActivity.class, - RealtimeDbChatActivity.class, - FirebaseDbPagingActivity.class, - ImageActivity.class, - }; - - private static final int[] DESCRIPTION_NAMES = new int[]{ - R.string.title_auth_activity, - R.string.title_anonymous_upgrade, - R.string.title_firestore_activity, - R.string.title_firestore_paging_activity, - R.string.title_realtime_database_activity, - R.string.title_realtime_database_paging_activity, - R.string.title_storage_activity - }; - - private static final int[] DESCRIPTION_IDS = new int[]{ - R.string.desc_auth, - R.string.desc_anonymous_upgrade, - R.string.desc_firestore, - R.string.desc_firestore_paging, - R.string.desc_realtime_database, - R.string.desc_realtime_database_paging, - R.string.desc_storage - }; - - @Override - public ActivityStarterHolder onCreateViewHolder(ViewGroup parent, int viewType) { - return new ActivityStarterHolder( - LayoutInflater.from(parent.getContext()) - .inflate(R.layout.activity_chooser_item, parent, false)); - } - - @Override - public void onBindViewHolder(ActivityStarterHolder holder, int position) { - holder.bind(CLASSES[position], DESCRIPTION_NAMES[position], DESCRIPTION_IDS[position]); - } - - @Override - public int getItemCount() { - return CLASSES.length; - } - } - - private static class ActivityStarterHolder extends RecyclerView.ViewHolder - implements View.OnClickListener { - private TextView mTitle; - private TextView mDescription; - - private Class mStarterClass; - - public ActivityStarterHolder(View itemView) { - super(itemView); - mTitle = itemView.findViewById(R.id.text1); - mDescription = itemView.findViewById(R.id.text2); - } - - private void bind(Class aClass, @StringRes int name, @StringRes int description) { - mStarterClass = aClass; - - mTitle.setText(name); - mDescription.setText(description); - itemView.setOnClickListener(this); - } - - @Override - public void onClick(View v) { - itemView.getContext().startActivity(new Intent(itemView.getContext(), mStarterClass)); - } - } -} diff --git a/composeapp/src/main/java/com/firebase/composeapp/CustomSlotsThemingDemoActivity.kt b/app/src/main/java/com/firebase/uidemo/CustomSlotsThemingDemoActivity.kt similarity index 99% rename from composeapp/src/main/java/com/firebase/composeapp/CustomSlotsThemingDemoActivity.kt rename to app/src/main/java/com/firebase/uidemo/CustomSlotsThemingDemoActivity.kt index 75e9cb7d5..bd5cc1088 100644 --- a/composeapp/src/main/java/com/firebase/composeapp/CustomSlotsThemingDemoActivity.kt +++ b/app/src/main/java/com/firebase/uidemo/CustomSlotsThemingDemoActivity.kt @@ -1,4 +1,4 @@ -package com.firebase.composeapp +package com.firebase.uidemo import android.os.Bundle import android.util.Log @@ -25,9 +25,9 @@ import com.firebase.ui.auth.compose.configuration.auth_provider.AuthProvider import com.firebase.ui.auth.compose.configuration.string_provider.LocalAuthUIStringProvider import com.firebase.ui.auth.compose.configuration.theme.AuthUIAsset import com.firebase.ui.auth.compose.configuration.theme.AuthUITheme -import com.firebase.ui.auth.compose.ui.screens.EmailAuthContentState -import com.firebase.ui.auth.compose.ui.screens.EmailAuthMode -import com.firebase.ui.auth.compose.ui.screens.EmailAuthScreen +import com.firebase.ui.auth.compose.ui.screens.email.EmailAuthContentState +import com.firebase.ui.auth.compose.ui.screens.email.EmailAuthMode +import com.firebase.ui.auth.compose.ui.screens.email.EmailAuthScreen import com.firebase.ui.auth.compose.ui.screens.phone.PhoneAuthContentState import com.firebase.ui.auth.compose.ui.screens.phone.PhoneAuthScreen import com.firebase.ui.auth.compose.ui.screens.phone.PhoneAuthStep diff --git a/app/src/main/java/com/firebase/uidemo/FirebaseUIDemo.java b/app/src/main/java/com/firebase/uidemo/FirebaseUIDemo.java deleted file mode 100644 index db33245f8..000000000 --- a/app/src/main/java/com/firebase/uidemo/FirebaseUIDemo.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.firebase.uidemo; - -import androidx.appcompat.app.AppCompatDelegate; -import androidx.multidex.MultiDexApplication; - -public class FirebaseUIDemo extends MultiDexApplication { - static { - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY); - } -} diff --git a/composeapp/src/main/java/com/firebase/composeapp/HighLevelApiDemoActivity.kt b/app/src/main/java/com/firebase/uidemo/HighLevelApiDemoActivity.kt similarity index 99% rename from composeapp/src/main/java/com/firebase/composeapp/HighLevelApiDemoActivity.kt rename to app/src/main/java/com/firebase/uidemo/HighLevelApiDemoActivity.kt index 3ec0d3a1e..22579a41a 100644 --- a/composeapp/src/main/java/com/firebase/composeapp/HighLevelApiDemoActivity.kt +++ b/app/src/main/java/com/firebase/uidemo/HighLevelApiDemoActivity.kt @@ -1,4 +1,4 @@ -package com.firebase.composeapp +package com.firebase.uidemo import android.os.Bundle import android.util.Log @@ -65,7 +65,7 @@ class HighLevelApiDemoActivity : ComponentActivity() { url = "https://temp-test-aa342.firebaseapp.com" handleCodeInApp = true setAndroidPackageName( - "com.firebase.composeapp", + "com.firebase.uidemo", true, null ) diff --git a/composeapp/src/main/java/com/firebase/composeapp/MainActivity.kt b/app/src/main/java/com/firebase/uidemo/MainActivity.kt similarity index 99% rename from composeapp/src/main/java/com/firebase/composeapp/MainActivity.kt rename to app/src/main/java/com/firebase/uidemo/MainActivity.kt index 039e20115..da3bb669c 100644 --- a/composeapp/src/main/java/com/firebase/composeapp/MainActivity.kt +++ b/app/src/main/java/com/firebase/uidemo/MainActivity.kt @@ -1,4 +1,4 @@ -package com.firebase.composeapp +package com.firebase.uidemo import android.content.Intent import android.os.Bundle @@ -36,7 +36,7 @@ import com.google.firebase.FirebaseApp */ class MainActivity : ComponentActivity() { companion object { - private const val USE_AUTH_EMULATOR = false + private const val USE_AUTH_EMULATOR = true private const val AUTH_EMULATOR_HOST = "10.0.2.2" private const val AUTH_EMULATOR_PORT = 9099 } diff --git a/app/src/main/java/com/firebase/uidemo/auth/AnonymousUpgradeActivity.java b/app/src/main/java/com/firebase/uidemo/auth/AnonymousUpgradeActivity.java deleted file mode 100644 index 4453c4cbd..000000000 --- a/app/src/main/java/com/firebase/uidemo/auth/AnonymousUpgradeActivity.java +++ /dev/null @@ -1,192 +0,0 @@ -package com.firebase.uidemo.auth; - -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.firebase.ui.auth.AuthUI; -import com.firebase.ui.auth.ErrorCodes; -import com.firebase.ui.auth.FirebaseAuthUIActivityResultContract; -import com.firebase.ui.auth.IdpResponse; -import com.firebase.ui.auth.data.model.FirebaseAuthUIAuthenticationResult; -import com.firebase.uidemo.R; -import com.firebase.uidemo.databinding.ActivityAnonymousUpgradeBinding; -import com.firebase.uidemo.util.ConfigurationUtils; -import com.google.android.gms.tasks.OnCompleteListener; -import com.google.android.gms.tasks.Task; -import com.google.firebase.auth.AuthCredential; -import com.google.firebase.auth.AuthResult; -import com.google.firebase.auth.FirebaseAuth; -import com.google.firebase.auth.FirebaseUser; - -import java.util.List; - -import androidx.activity.result.ActivityResultCallback; -import androidx.activity.result.ActivityResultLauncher; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; - -public class AnonymousUpgradeActivity extends AppCompatActivity - implements ActivityResultCallback { - - private static final String TAG = "AccountLink"; - - private ActivityAnonymousUpgradeBinding mBinding; - - private AuthCredential mPendingCredential; - - private final ActivityResultLauncher signIn = - registerForActivityResult(new FirebaseAuthUIActivityResultContract(), this); - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mBinding = ActivityAnonymousUpgradeBinding.inflate(getLayoutInflater()); - setContentView(mBinding.getRoot()); - - updateUI(); - - // Got here from AuthUIActivity, and we need to deal with a merge conflict - // Occurs after catching an email link - IdpResponse response = IdpResponse.fromResultIntent(getIntent()); - if (response != null) { - handleSignInResult(ErrorCodes.ANONYMOUS_UPGRADE_MERGE_CONFLICT, response); - } - - mBinding.anonSignIn.setOnClickListener(view -> signInAnonymously()); - mBinding.beginFlow.setOnClickListener(view -> startAuthUI()); - mBinding.resolveMerge.setOnClickListener(view -> resolveMerge()); - mBinding.signOut.setOnClickListener(view -> signOut()); - } - - public void signInAnonymously() { - FirebaseAuth.getInstance().signInAnonymously() - .addOnCompleteListener(this, task -> { - updateUI(); - - if (task.isSuccessful()) { - setStatus("Signed in anonymously as user " - + getUserIdentifier(task.getResult().getUser())); - } else { - setStatus("Anonymous sign in failed."); - } - }); - } - - public void startAuthUI() { - List providers = ConfigurationUtils.getConfiguredProviders(this); - Intent signInIntent = AuthUI.getInstance().createSignInIntentBuilder() - .setLogo(R.drawable.firebase_auth_120dp) - .setAvailableProviders(providers) - .enableAnonymousUsersAutoUpgrade() - .build(); - signIn.launch(signInIntent); - } - - public void resolveMerge() { - if (mPendingCredential == null) { - Toast.makeText(this, "Nothing to resolve.", Toast.LENGTH_SHORT).show(); - return; - } - - // TODO: Show how to do good data moving - - FirebaseAuth.getInstance().signInWithCredential(mPendingCredential) - .addOnCompleteListener(this, task -> { - mPendingCredential = null; - updateUI(); - - if (task.isSuccessful()) { - setStatus("Signed in as " + getUserIdentifier(task.getResult() - .getUser())); - } else { - Log.w(TAG, "Merge failed", task.getException()); - setStatus("Failed to resolve merge conflict, see logs."); - } - }); - } - - public void signOut() { - AuthUI.getInstance().signOut(this) - .addOnCompleteListener(task -> { - setStatus(null); - updateUI(); - }); - } - - private void handleSignInResult(int resultCode, @Nullable IdpResponse response) { - if (response == null) { - // User pressed back button - return; - } - if (resultCode == RESULT_OK) { - setStatus("Signed in as " + getUserIdentifier(FirebaseAuth.getInstance() - .getCurrentUser())); - } else if (response.getError().getErrorCode() == ErrorCodes - .ANONYMOUS_UPGRADE_MERGE_CONFLICT) { - setStatus("Merge conflict: user already exists."); - mBinding.resolveMerge.setEnabled(true); - mPendingCredential = response.getCredentialForLinking(); - } else { - Toast.makeText(this, "Auth error, see logs", Toast.LENGTH_SHORT).show(); - Log.w(TAG, "Error: " + response.getError().getMessage(), response.getError()); - } - - updateUI(); - } - - private void updateUI() { - FirebaseUser currentUser = FirebaseAuth.getInstance().getCurrentUser(); - - if (currentUser == null) { - // Not signed in - mBinding.anonSignIn.setEnabled(true); - mBinding.beginFlow.setEnabled(false); - mBinding.resolveMerge.setEnabled(false); - mBinding.signOut.setEnabled(false); - } else if (mPendingCredential == null && currentUser.isAnonymous()) { - // Anonymous user, waiting for linking - mBinding.anonSignIn.setEnabled(false); - mBinding.beginFlow.setEnabled(true); - mBinding.resolveMerge.setEnabled(false); - mBinding.signOut.setEnabled(true); - } else if (mPendingCredential == null && !currentUser.isAnonymous()) { - // Fully signed in - mBinding.anonSignIn.setEnabled(false); - mBinding.beginFlow.setEnabled(false); - mBinding.resolveMerge.setEnabled(false); - mBinding.signOut.setEnabled(true); - } else if (mPendingCredential != null) { - // Signed in anonymous, awaiting merge conflict - mBinding.anonSignIn.setEnabled(false); - mBinding.beginFlow.setEnabled(false); - mBinding.resolveMerge.setEnabled(true); - mBinding.signOut.setEnabled(true); - } - } - - private void setStatus(String message) { - mBinding.statusText.setText(message); - } - - private String getUserIdentifier(FirebaseUser user) { - if (user.isAnonymous()) { - return user.getUid(); - } else if (!TextUtils.isEmpty(user.getEmail())) { - return user.getEmail(); - } else if (!TextUtils.isEmpty(user.getPhoneNumber())) { - return user.getPhoneNumber(); - } else { - return "unknown"; - } - } - - @Override - public void onActivityResult(@NonNull FirebaseAuthUIAuthenticationResult result) { - handleSignInResult(result.getResultCode(), result.getIdpResponse()); - } -} diff --git a/app/src/main/java/com/firebase/uidemo/auth/AuthUiActivity.java b/app/src/main/java/com/firebase/uidemo/auth/AuthUiActivity.java deleted file mode 100644 index 0a8d44cd3..000000000 --- a/app/src/main/java/com/firebase/uidemo/auth/AuthUiActivity.java +++ /dev/null @@ -1,438 +0,0 @@ -/* - * Copyright 2016 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.firebase.uidemo.auth; - -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.util.Log; -import android.view.View; -import android.widget.CompoundButton; -import android.widget.CompoundButton.OnCheckedChangeListener; - -import com.firebase.ui.auth.AuthMethodPickerLayout; -import com.firebase.ui.auth.AuthUI; -import com.firebase.ui.auth.AuthUI.IdpConfig; -import com.firebase.ui.auth.ErrorCodes; -import com.firebase.ui.auth.FirebaseAuthUIActivityResultContract; -import com.firebase.ui.auth.IdpResponse; -import com.firebase.ui.auth.data.model.FirebaseAuthUIAuthenticationResult; -import com.firebase.ui.auth.util.ExtraConstants; -import com.firebase.uidemo.R; -import com.firebase.uidemo.databinding.AuthUiLayoutBinding; -import com.firebase.uidemo.util.ConfigurationUtils; -import com.google.android.gms.common.Scopes; -import com.google.android.gms.tasks.OnCompleteListener; -import com.google.android.gms.tasks.Task; -import com.google.android.material.snackbar.Snackbar; -import com.google.firebase.auth.ActionCodeSettings; -import com.google.firebase.auth.AuthResult; -import com.google.firebase.auth.FirebaseAuth; - -import java.util.ArrayList; -import java.util.List; - -import androidx.activity.result.ActivityResultCallback; -import androidx.activity.result.ActivityResultLauncher; -import androidx.annotation.DrawableRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.StringRes; -import androidx.annotation.StyleRes; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.app.AppCompatDelegate; - -public class AuthUiActivity extends AppCompatActivity - implements ActivityResultCallback { - private static final String TAG = "AuthUiActivity"; - - private static final String GOOGLE_TOS_URL = "https://www.google.com/policies/terms/"; - private static final String FIREBASE_TOS_URL = "https://firebase.google.com/terms/"; - private static final String GOOGLE_PRIVACY_POLICY_URL = "https://www.google" + - ".com/policies/privacy/"; - private static final String FIREBASE_PRIVACY_POLICY_URL = "https://firebase.google" + - ".com/terms/analytics/#7_privacy"; - - private static final int RC_SIGN_IN = 100; - - private AuthUiLayoutBinding mBinding; - - private final ActivityResultLauncher signIn = - registerForActivityResult(new FirebaseAuthUIActivityResultContract(), this); - - @NonNull - public static Intent createIntent(@NonNull Context context) { - return new Intent(context, AuthUiActivity.class); - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mBinding = AuthUiLayoutBinding.inflate(getLayoutInflater()); - setContentView(mBinding.getRoot()); - - // Workaround for vector drawables on API 19 - AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); - - if (ConfigurationUtils.isGoogleMisconfigured(this)) { - mBinding.googleProvider.setChecked(false); - mBinding.googleProvider.setEnabled(false); - mBinding.googleProvider.setText(R.string.google_label_missing_config); - setGoogleScopesEnabled(false); - } else { - setGoogleScopesEnabled(mBinding.googleProvider.isChecked()); - mBinding.googleProvider.setOnCheckedChangeListener((compoundButton, checked) -> setGoogleScopesEnabled(checked)); - } - - if (ConfigurationUtils.isFacebookMisconfigured(this)) { - mBinding.facebookProvider.setChecked(false); - mBinding.facebookProvider.setEnabled(false); - mBinding.facebookProvider.setText(R.string.facebook_label_missing_config); - setFacebookPermissionsEnabled(false); - } else { - setFacebookPermissionsEnabled(mBinding.facebookProvider.isChecked()); - mBinding.facebookProvider.setOnCheckedChangeListener((compoundButton, checked) -> setFacebookPermissionsEnabled(checked)); - } - - mBinding.emailLinkProvider.setOnCheckedChangeListener((buttonView, isChecked) -> flipPasswordProviderCheckbox(isChecked)); - - mBinding.emailProvider.setOnCheckedChangeListener((buttonView, isChecked) -> flipEmailLinkProviderCheckbox(isChecked)); - - mBinding.emailLinkProvider.setChecked(false); - mBinding.emailProvider.setChecked(true); - - // The custom layout in this app only supports Email and Google providers. - mBinding.customLayout.setOnCheckedChangeListener((compoundButton, checked) -> { - if (checked) { - mBinding.googleProvider.setChecked(true); - mBinding.emailProvider.setChecked(true); - - mBinding.facebookProvider.setChecked(false); - mBinding.twitterProvider.setChecked(false); - mBinding.emailLinkProvider.setChecked(false); - mBinding.phoneProvider.setChecked(false); - mBinding.anonymousProvider.setChecked(false); - mBinding.microsoftProvider.setChecked(false); - mBinding.yahooProvider.setChecked(false); - mBinding.appleProvider.setChecked(false); - mBinding.githubProvider.setChecked(false); - } - }); - - // useEmulator can't be reversed until the FirebaseApp is cleared, so we make this - // checkbox "sticky" until the app is restarted - mBinding.useAuthEmulator.setOnCheckedChangeListener((buttonView, isChecked) -> { - if (isChecked) { - mBinding.useAuthEmulator.setEnabled(false); - } - }); - - mBinding.signIn.setOnClickListener(view -> signIn()); - - if (ConfigurationUtils.isGoogleMisconfigured(this) - || ConfigurationUtils.isFacebookMisconfigured(this)) { - showSnackbar(R.string.configuration_required); - } - - catchEmailLinkSignIn(); - } - - public void catchEmailLinkSignIn() { - if (getIntent().getExtras() == null) { - return; - } - String link = getIntent().getExtras().getString(ExtraConstants.EMAIL_LINK_SIGN_IN); - if (link != null) { - signInWithEmailLink(link); - } - } - - public void flipPasswordProviderCheckbox(boolean emailLinkProviderIsChecked) { - if (emailLinkProviderIsChecked) { - mBinding.emailProvider.setChecked(false); - } - } - - public void flipEmailLinkProviderCheckbox(boolean passwordProviderIsChecked) { - if (passwordProviderIsChecked) { - mBinding.emailLinkProvider.setChecked(false); - } - } - - public void signIn() { - signIn.launch(getSignInIntent(/*link=*/null)); - } - - public void signInWithEmailLink(@Nullable String link) { - signIn.launch(getSignInIntent(link)); - } - - @NonNull - public AuthUI getAuthUI() { - AuthUI authUI = AuthUI.getInstance(); - if (mBinding.useAuthEmulator.isChecked()) { - authUI.useEmulator("10.0.2.2", 9099); - } - - return authUI; - } - - private Intent getSignInIntent(@Nullable String link) { - AuthUI.SignInIntentBuilder builder = getAuthUI().createSignInIntentBuilder() - .setTheme(getSelectedTheme()) - .setLogo(getSelectedLogo()) - .setAvailableProviders(getSelectedProviders()) - .setCredentialManagerEnabled(mBinding.credentialSelectorEnabled.isChecked()); - - if (mBinding.customLayout.isChecked()) { - AuthMethodPickerLayout customLayout = new AuthMethodPickerLayout - .Builder(R.layout.auth_method_picker_custom_layout) - .setGoogleButtonId(R.id.custom_google_signin_button) - .setEmailButtonId(R.id.custom_email_signin_clickable_text) - .setTosAndPrivacyPolicyId(R.id.custom_tos_pp) - .build(); - - builder.setTheme(R.style.CustomTheme); - builder.setAuthMethodPickerLayout(customLayout); - } - - if (getSelectedTosUrl() != null && getSelectedPrivacyPolicyUrl() != null) { - builder.setTosAndPrivacyPolicyUrls( - getSelectedTosUrl(), - getSelectedPrivacyPolicyUrl()); - } - - if (link != null) { - builder.setEmailLink(link); - } - - FirebaseAuth auth = FirebaseAuth.getInstance(); - - if (auth.getCurrentUser() != null && auth.getCurrentUser().isAnonymous()) { - builder.enableAnonymousUsersAutoUpgrade(); - } - - builder.setAlwaysShowSignInMethodScreen(true); - return builder.build(); - } - - @Override - protected void onResume() { - super.onResume(); - FirebaseAuth auth = FirebaseAuth.getInstance(); - if (auth.getCurrentUser() != null && getIntent().getExtras() == null) { - startSignedInActivity(null); - finish(); - } - } - - private void handleSignInResponse(int resultCode, @Nullable IdpResponse response) { - // Successfully signed in - if (resultCode == RESULT_OK) { - startSignedInActivity(response); - finish(); - } else { - // Sign in failed - if (response == null) { - // User pressed back button - showSnackbar(R.string.sign_in_cancelled); - return; - } - - if (response.getError().getErrorCode() == ErrorCodes.NO_NETWORK) { - showSnackbar(R.string.no_internet_connection); - return; - } - - if (response.getError().getErrorCode() == ErrorCodes.ANONYMOUS_UPGRADE_MERGE_CONFLICT) { - Intent intent = new Intent(this, AnonymousUpgradeActivity.class).putExtra - (ExtraConstants.IDP_RESPONSE, response); - startActivity(intent); - } - - if (response.getError().getErrorCode() == ErrorCodes.ERROR_USER_DISABLED) { - showSnackbar(R.string.account_disabled); - return; - } - - showSnackbar(R.string.unknown_error); - Log.e(TAG, "Sign-in error: ", response.getError()); - } - } - - private void startSignedInActivity(@Nullable IdpResponse response) { - startActivity(SignedInActivity.createIntent(this, response)); - } - - @StyleRes - private int getSelectedTheme() { - if (mBinding.greenTheme.isChecked()) { - return R.style.GreenTheme; - } - - if (mBinding.appTheme.isChecked()) { - return R.style.AppTheme; - } - - return AuthUI.getDefaultTheme(); - } - - @DrawableRes - private int getSelectedLogo() { - if (mBinding.firebaseLogo.isChecked()) { - return R.drawable.firebase_auth_120dp; - } else if (mBinding.googleLogo.isChecked()) { - return R.drawable.ic_googleg_color_144dp; - } - return AuthUI.NO_LOGO; - } - - private List getSelectedProviders() { - List selectedProviders = new ArrayList<>(); - - if (mBinding.googleProvider.isChecked()) { - selectedProviders.add( - new IdpConfig.GoogleBuilder().setScopes(getGoogleScopes()).build()); - } - - if (mBinding.facebookProvider.isChecked()) { - selectedProviders.add(new IdpConfig.FacebookBuilder() - .setPermissions(getFacebookPermissions()) - .build()); - } - - if (mBinding.emailProvider.isChecked()) { - selectedProviders.add(new IdpConfig.EmailBuilder() - .setRequireName(mBinding.requireName.isChecked()) - .setAllowNewAccounts(mBinding.allowNewEmailAccounts.isChecked()) - .build()); - } - - if (mBinding.emailLinkProvider.isChecked()) { - ActionCodeSettings actionCodeSettings = ActionCodeSettings.newBuilder() - .setAndroidPackageName("com.firebase.uidemo", true, null) - .setHandleCodeInApp(true) - .setUrl("https://google.com") - .build(); - - selectedProviders.add(new IdpConfig.EmailBuilder() - .setAllowNewAccounts(mBinding.allowNewEmailAccounts.isChecked()) - .setActionCodeSettings(actionCodeSettings) - .enableEmailLinkSignIn() - .build()); - } - - if (mBinding.phoneProvider.isChecked()) { - selectedProviders.add(new IdpConfig.PhoneBuilder().build()); - } - - if (mBinding.anonymousProvider.isChecked()) { - selectedProviders.add(new IdpConfig.AnonymousBuilder().build()); - } - - if (mBinding.twitterProvider.isChecked()) { - selectedProviders.add(new IdpConfig.TwitterBuilder().build()); - } - - if (mBinding.microsoftProvider.isChecked()) { - selectedProviders.add(new IdpConfig.MicrosoftBuilder().build()); - } - - if (mBinding.yahooProvider.isChecked()) { - selectedProviders.add(new IdpConfig.YahooBuilder().build()); - } - - if (mBinding.appleProvider.isChecked()) { - selectedProviders.add(new IdpConfig.AppleBuilder().build()); - } - - if (mBinding.githubProvider.isChecked()) { - selectedProviders.add(new IdpConfig.GitHubBuilder().build()); - } - - return selectedProviders; - } - - @Nullable - private String getSelectedTosUrl() { - if (mBinding.googleTosPrivacy.isChecked()) { - return GOOGLE_TOS_URL; - } - - if (mBinding.firebaseTosPrivacy.isChecked()) { - return FIREBASE_TOS_URL; - } - - return null; - } - - @Nullable - private String getSelectedPrivacyPolicyUrl() { - if (mBinding.googleTosPrivacy.isChecked()) { - return GOOGLE_PRIVACY_POLICY_URL; - } - - if (mBinding.firebaseTosPrivacy.isChecked()) { - return FIREBASE_PRIVACY_POLICY_URL; - } - - return null; - } - - private void setGoogleScopesEnabled(boolean enabled) { - mBinding.googleScopesHeader.setEnabled(enabled); - mBinding.googleScopeDriveFile.setEnabled(enabled); - mBinding.googleScopeYoutubeData.setEnabled(enabled); - } - - private void setFacebookPermissionsEnabled(boolean enabled) { - mBinding.facebookPermissionsHeader.setEnabled(enabled); - mBinding.facebookPermissionFriends.setEnabled(enabled); - mBinding.facebookPermissionPhotos.setEnabled(enabled); - } - - private List getGoogleScopes() { - List result = new ArrayList<>(); - if (mBinding.googleScopeYoutubeData.isChecked()) { - result.add("https://www.googleapis.com/auth/youtube.readonly"); - } - if (mBinding.googleScopeDriveFile.isChecked()) { - result.add(Scopes.DRIVE_FILE); - } - return result; - } - - private List getFacebookPermissions() { - List result = new ArrayList<>(); - if (mBinding.facebookPermissionFriends.isChecked()) { - result.add("user_friends"); - } - if (mBinding.facebookPermissionPhotos.isChecked()) { - result.add("user_photos"); - } - return result; - } - - private void showSnackbar(@StringRes int errorMessageRes) { - Snackbar.make(mBinding.getRoot(), errorMessageRes, Snackbar.LENGTH_LONG).show(); - } - - @Override - public void onActivityResult(@NonNull FirebaseAuthUIAuthenticationResult result) { - // Successfully signed in - IdpResponse response = result.getIdpResponse(); - handleSignInResponse(result.getResultCode(), response); - } -} diff --git a/app/src/main/java/com/firebase/uidemo/auth/SignedInActivity.java b/app/src/main/java/com/firebase/uidemo/auth/SignedInActivity.java deleted file mode 100644 index 9749f5c86..000000000 --- a/app/src/main/java/com/firebase/uidemo/auth/SignedInActivity.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 2016 Google Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the - * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either - * express or implied. See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.firebase.uidemo.auth; - -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.os.Bundle; -import android.text.TextUtils; -import android.util.Log; -import android.view.View; -import android.widget.TextView; - -import com.firebase.ui.auth.AuthUI; -import com.firebase.ui.auth.IdpResponse; -import com.firebase.ui.auth.util.ExtraConstants; -import com.firebase.uidemo.R; -import com.firebase.uidemo.databinding.SignedInLayoutBinding; -import com.firebase.uidemo.storage.GlideApp; -import com.google.android.gms.tasks.OnCompleteListener; -import com.google.android.gms.tasks.Task; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; -import com.google.android.material.snackbar.Snackbar; -import com.google.firebase.auth.EmailAuthProvider; -import com.google.firebase.auth.FacebookAuthProvider; -import com.google.firebase.auth.FirebaseAuth; -import com.google.firebase.auth.FirebaseAuthProvider; -import com.google.firebase.auth.FirebaseUser; -import com.google.firebase.auth.GoogleAuthProvider; -import com.google.firebase.auth.PhoneAuthProvider; -import com.google.firebase.auth.TwitterAuthProvider; -import com.google.firebase.auth.UserInfo; - -import java.util.ArrayList; -import java.util.List; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.StringRes; -import androidx.appcompat.app.AppCompatActivity; - -import static com.firebase.ui.auth.AuthUI.EMAIL_LINK_PROVIDER; - -public class SignedInActivity extends AppCompatActivity { - private static final String TAG = "SignedInActivity"; - - private SignedInLayoutBinding mBinding; - - @NonNull - public static Intent createIntent(@NonNull Context context, @Nullable IdpResponse response) { - return new Intent().setClass(context, SignedInActivity.class) - .putExtra(ExtraConstants.IDP_RESPONSE, response); - } - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - FirebaseUser currentUser = FirebaseAuth.getInstance().getCurrentUser(); - if (currentUser == null) { - startActivity(AuthUiActivity.createIntent(this)); - finish(); - return; - } - - IdpResponse response = getIntent().getParcelableExtra(ExtraConstants.IDP_RESPONSE); - - mBinding = SignedInLayoutBinding.inflate(getLayoutInflater()); - setContentView(mBinding.getRoot()); - populateProfile(response); - populateIdpToken(response); - - mBinding.deleteAccount.setOnClickListener(view -> deleteAccountClicked()); - - mBinding.signOut.setOnClickListener(view -> signOut()); - } - - public void signOut() { - AuthUI.getInstance() - .signOut(this) - .addOnCompleteListener(task -> { - if (task.isSuccessful()) { - startActivity(AuthUiActivity.createIntent(SignedInActivity.this)); - finish(); - } else { - Log.w(TAG, "signOut:failure", task.getException()); - showSnackbar(R.string.sign_out_failed); - } - }); - } - - public void deleteAccountClicked() { - new MaterialAlertDialogBuilder(this) - .setMessage("Are you sure you want to delete this account?") - .setPositiveButton("Yes, nuke it!", (dialogInterface, i) -> deleteAccount()) - .setNegativeButton("No", null) - .show(); - } - - private void deleteAccount() { - AuthUI.getInstance() - .delete(this) - .addOnCompleteListener(this, task -> { - if (task.isSuccessful()) { - startActivity(AuthUiActivity.createIntent(SignedInActivity.this)); - finish(); - } else { - showSnackbar(R.string.delete_account_failed); - } - }); - } - - private void populateProfile(@Nullable IdpResponse response) { - FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser(); - if (user.getPhotoUrl() != null) { - GlideApp.with(this) - .load(user.getPhotoUrl()) - .fitCenter() - .into(mBinding.userProfilePicture); - } - - mBinding.userEmail.setText( - TextUtils.isEmpty(user.getEmail()) ? "No email" : user.getEmail()); - mBinding.userPhoneNumber.setText( - TextUtils.isEmpty(user.getPhoneNumber()) ? "No phone number" : user.getPhoneNumber()); - mBinding.userDisplayName.setText( - TextUtils.isEmpty(user.getDisplayName()) ? "No display name" : user.getDisplayName()); - - if (response == null) { - mBinding.userIsNew.setVisibility(View.GONE); - } else { - mBinding.userIsNew.setVisibility(View.VISIBLE); - mBinding.userIsNew.setText(response.isNewUser() ? "New user" : "Existing user"); - } - - List providers = new ArrayList<>(); - if (user.getProviderData().isEmpty()) { - providers.add(getString(R.string.providers_anonymous)); - } else { - for (UserInfo info : user.getProviderData()) { - switch (info.getProviderId()) { - case GoogleAuthProvider.PROVIDER_ID: - providers.add(getString(R.string.providers_google)); - break; - case FacebookAuthProvider.PROVIDER_ID: - providers.add(getString(R.string.providers_facebook)); - break; - case TwitterAuthProvider.PROVIDER_ID: - providers.add(getString(R.string.providers_twitter)); - break; - case EmailAuthProvider.PROVIDER_ID: - providers.add(getString(R.string.providers_email)); - break; - case PhoneAuthProvider.PROVIDER_ID: - providers.add(getString(R.string.providers_phone)); - break; - case EMAIL_LINK_PROVIDER: - providers.add(getString(R.string.providers_email_link)); - break; - case FirebaseAuthProvider.PROVIDER_ID: - // Ignore this provider, it's not very meaningful - break; - default: - providers.add(info.getProviderId()); - } - } - } - - mBinding.userEnabledProviders.setText(getString(R.string.used_providers, providers)); - } - - private void populateIdpToken(@Nullable IdpResponse response) { - String token = null; - String secret = null; - if (response != null) { - token = response.getIdpToken(); - secret = response.getIdpSecret(); - } - - View idpTokenLayout = findViewById(R.id.idp_token_layout); - if (token == null) { - idpTokenLayout.setVisibility(View.GONE); - } else { - idpTokenLayout.setVisibility(View.VISIBLE); - ((TextView) findViewById(R.id.idp_token)).setText(token); - } - - View idpSecretLayout = findViewById(R.id.idp_secret_layout); - if (secret == null) { - idpSecretLayout.setVisibility(View.GONE); - } else { - idpSecretLayout.setVisibility(View.VISIBLE); - ((TextView) findViewById(R.id.idp_secret)).setText(secret); - } - } - - private void showSnackbar(@StringRes int errorMessageRes) { - Snackbar.make(mBinding.getRoot(), errorMessageRes, Snackbar.LENGTH_LONG).show(); - } -} diff --git a/app/src/main/java/com/firebase/uidemo/database/AbstractChat.java b/app/src/main/java/com/firebase/uidemo/database/AbstractChat.java deleted file mode 100644 index 447abd197..000000000 --- a/app/src/main/java/com/firebase/uidemo/database/AbstractChat.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.firebase.uidemo.database; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -/** - * Common interface for chat messages, helps share code between RTDB and Firestore examples. - */ -public abstract class AbstractChat { - - @Nullable - public abstract String getName(); - - public abstract void setName(@Nullable String name); - - @Nullable - public abstract String getMessage(); - - public abstract void setMessage(@Nullable String message); - - @NonNull - public abstract String getUid(); - - public abstract void setUid(@NonNull String uid); - - @Override - public abstract boolean equals(@Nullable Object obj); - - @Override - public abstract int hashCode(); - -} diff --git a/app/src/main/java/com/firebase/uidemo/database/ChatHolder.java b/app/src/main/java/com/firebase/uidemo/database/ChatHolder.java deleted file mode 100644 index b783415c5..000000000 --- a/app/src/main/java/com/firebase/uidemo/database/ChatHolder.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.firebase.uidemo.database; - -import android.graphics.PorterDuff; -import android.graphics.drawable.GradientDrawable; -import android.graphics.drawable.RotateDrawable; -import android.view.Gravity; -import android.view.View; -import android.widget.FrameLayout; -import android.widget.LinearLayout; -import android.widget.RelativeLayout; -import android.widget.TextView; - -import com.firebase.uidemo.R; -import com.google.firebase.auth.FirebaseAuth; -import com.google.firebase.auth.FirebaseUser; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.content.ContextCompat; -import androidx.recyclerview.widget.RecyclerView; - -public class ChatHolder extends RecyclerView.ViewHolder { - private final TextView mNameField; - private final TextView mTextField; - private final FrameLayout mLeftArrow; - private final FrameLayout mRightArrow; - private final RelativeLayout mMessageContainer; - private final LinearLayout mMessage; - private final int mGreen300; - private final int mGray300; - - public ChatHolder(@NonNull View itemView) { - super(itemView); - mNameField = itemView.findViewById(R.id.name_text); - mTextField = itemView.findViewById(R.id.message_text); - mLeftArrow = itemView.findViewById(R.id.left_arrow); - mRightArrow = itemView.findViewById(R.id.right_arrow); - mMessageContainer = itemView.findViewById(R.id.message_container); - mMessage = itemView.findViewById(R.id.message); - mGreen300 = ContextCompat.getColor(itemView.getContext(), R.color.material_green_300); - mGray300 = ContextCompat.getColor(itemView.getContext(), R.color.material_gray_300); - } - - public void bind(@NonNull AbstractChat chat) { - setName(chat.getName()); - setMessage(chat.getMessage()); - - FirebaseUser currentUser = FirebaseAuth.getInstance().getCurrentUser(); - setIsSender(currentUser != null && chat.getUid().equals(currentUser.getUid())); - } - - private void setName(@Nullable String name) { - mNameField.setText(name); - } - - private void setMessage(@Nullable String text) { - mTextField.setText(text); - } - - private void setIsSender(boolean isSender) { - final int color; - if (isSender) { - color = mGreen300; - mLeftArrow.setVisibility(View.GONE); - mRightArrow.setVisibility(View.VISIBLE); - mMessageContainer.setGravity(Gravity.END); - } else { - color = mGray300; - mLeftArrow.setVisibility(View.VISIBLE); - mRightArrow.setVisibility(View.GONE); - mMessageContainer.setGravity(Gravity.START); - } - - ((GradientDrawable) mMessage.getBackground()).setColor(color); - ((RotateDrawable) mLeftArrow.getBackground()).getDrawable() - .setColorFilter(color, PorterDuff.Mode.SRC); - ((RotateDrawable) mRightArrow.getBackground()).getDrawable() - .setColorFilter(color, PorterDuff.Mode.SRC); - } -} diff --git a/app/src/main/java/com/firebase/uidemo/database/firestore/Chat.java b/app/src/main/java/com/firebase/uidemo/database/firestore/Chat.java deleted file mode 100644 index ef95a90ed..000000000 --- a/app/src/main/java/com/firebase/uidemo/database/firestore/Chat.java +++ /dev/null @@ -1,104 +0,0 @@ -package com.firebase.uidemo.database.firestore; - -import com.firebase.uidemo.database.AbstractChat; -import com.google.firebase.firestore.IgnoreExtraProperties; -import com.google.firebase.firestore.ServerTimestamp; - -import java.util.Date; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -@IgnoreExtraProperties -public class Chat extends AbstractChat { - private String mName; - private String mMessage; - private String mUid; - private Date mTimestamp; - - public Chat() { - // Needed for Firebase - } - - public Chat(@Nullable String name, @Nullable String message, @NonNull String uid) { - mName = name; - mMessage = message; - mUid = uid; - } - - @Override - @Nullable - public String getName() { - return mName; - } - - @Override - public void setName(@Nullable String name) { - mName = name; - } - - @Override - @Nullable - public String getMessage() { - return mMessage; - } - - @Override - public void setMessage(@Nullable String message) { - mMessage = message; - } - - @Override - @NonNull - public String getUid() { - return mUid; - } - - @Override - public void setUid(@NonNull String uid) { - mUid = uid; - } - - @ServerTimestamp - @Nullable - public Date getTimestamp() { - return mTimestamp; - } - - public void setTimestamp(@Nullable Date timestamp) { - mTimestamp = timestamp; - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Chat chat = (Chat) o; - - return mTimestamp.equals(chat.mTimestamp) - && mUid.equals(chat.mUid) - && (mName == null ? chat.mName == null : mName.equals(chat.mName)) - && (mMessage == null ? chat.mMessage == null : mMessage.equals(chat.mMessage)); - } - - @Override - public int hashCode() { - int result = mName == null ? 0 : mName.hashCode(); - result = 31 * result + (mMessage == null ? 0 : mMessage.hashCode()); - result = 31 * result + mUid.hashCode(); - result = 31 * result + mTimestamp.hashCode(); - return result; - } - - @NonNull - @Override - public String toString() { - return "Chat{" + - "mName='" + mName + '\'' + - ", mMessage='" + mMessage + '\'' + - ", mUid='" + mUid + '\'' + - ", mTimestamp=" + mTimestamp + - '}'; - } -} diff --git a/app/src/main/java/com/firebase/uidemo/database/firestore/FirestoreChatActivity.java b/app/src/main/java/com/firebase/uidemo/database/firestore/FirestoreChatActivity.java deleted file mode 100644 index 59af0408b..000000000 --- a/app/src/main/java/com/firebase/uidemo/database/firestore/FirestoreChatActivity.java +++ /dev/null @@ -1,168 +0,0 @@ -package com.firebase.uidemo.database.firestore; - -import android.annotation.SuppressLint; -import android.os.Bundle; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Toast; - -import com.firebase.ui.auth.util.ui.ImeHelper; -import com.firebase.ui.firestore.FirestoreRecyclerAdapter; -import com.firebase.ui.firestore.FirestoreRecyclerOptions; -import com.firebase.uidemo.R; -import com.firebase.uidemo.database.ChatHolder; -import com.firebase.uidemo.databinding.ActivityChatBinding; -import com.firebase.uidemo.util.SignInResultNotifier; -import com.google.android.gms.tasks.OnFailureListener; -import com.google.firebase.auth.FirebaseAuth; -import com.google.firebase.firestore.CollectionReference; -import com.google.firebase.firestore.FirebaseFirestore; -import com.google.firebase.firestore.Query; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -/** - * Class demonstrating how to setup a {@link RecyclerView} with an adapter while taking sign-in - * states into consideration. Also demonstrates adding data to a ref and then reading it back using - * the {@link FirestoreRecyclerAdapter} to build a simple chat app. - *

- * For a general intro to the RecyclerView, see Creating - * Lists. - */ -@SuppressLint("RestrictedApi") -public class FirestoreChatActivity extends AppCompatActivity - implements FirebaseAuth.AuthStateListener { - private static final String TAG = "FirestoreChatActivity"; - - private static final CollectionReference sChatCollection = - FirebaseFirestore.getInstance().collection("chats"); - /** Get the last 50 chat messages ordered by timestamp . */ - private static final Query sChatQuery = - sChatCollection.orderBy("timestamp", Query.Direction.DESCENDING).limit(50); - - static { - FirebaseFirestore.setLoggingEnabled(true); - } - - private ActivityChatBinding mBinding; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mBinding = ActivityChatBinding.inflate(getLayoutInflater()); - setContentView(mBinding.getRoot()); - - LinearLayoutManager manager = new LinearLayoutManager(this); - manager.setReverseLayout(true); - manager.setStackFromEnd(true); - - mBinding.messagesList.setHasFixedSize(true); - mBinding.messagesList.setLayoutManager(manager); - - mBinding.messagesList.addOnLayoutChangeListener((view, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> { - if (bottom < oldBottom) { - mBinding.messagesList.postDelayed(() -> mBinding.messagesList.smoothScrollToPosition( - 0), 100); - } - }); - - ImeHelper.setImeOnDoneListener(mBinding.messageEdit, () -> onSendClick()); - - mBinding.sendButton.setOnClickListener(view -> onSendClick()); - } - - @Override - public void onStart() { - super.onStart(); - if (isSignedIn()) { - attachRecyclerViewAdapter(); - } - FirebaseAuth.getInstance().addAuthStateListener(this); - } - - @Override - protected void onStop() { - super.onStop(); - FirebaseAuth.getInstance().removeAuthStateListener(this); - } - - @Override - public void onAuthStateChanged(@NonNull FirebaseAuth auth) { - mBinding.sendButton.setEnabled(isSignedIn()); - mBinding.messageEdit.setEnabled(isSignedIn()); - - if (isSignedIn()) { - attachRecyclerViewAdapter(); - } else { - Toast.makeText(this, R.string.signing_in, Toast.LENGTH_SHORT).show(); - auth.signInAnonymously().addOnCompleteListener(new SignInResultNotifier(this)); - } - } - - private boolean isSignedIn() { - return FirebaseAuth.getInstance().getCurrentUser() != null; - } - - private void attachRecyclerViewAdapter() { - final RecyclerView.Adapter adapter = newAdapter(); - - // Scroll to bottom on new messages - adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { - @Override - public void onItemRangeInserted(int positionStart, int itemCount) { - mBinding.messagesList.smoothScrollToPosition(0); - } - }); - - mBinding.messagesList.setAdapter(adapter); - } - - public void onSendClick() { - String uid = FirebaseAuth.getInstance().getCurrentUser().getUid(); - String name = "User " + uid.substring(0, 6); - - onAddMessage(new Chat(name, mBinding.messageEdit.getText().toString(), uid)); - - mBinding.messageEdit.setText(""); - } - - @NonNull - private RecyclerView.Adapter newAdapter() { - FirestoreRecyclerOptions options = - new FirestoreRecyclerOptions.Builder() - .setQuery(sChatQuery, Chat.class) - .setLifecycleOwner(this) - .build(); - - return new FirestoreRecyclerAdapter(options) { - @NonNull - @Override - public ChatHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - return new ChatHolder(LayoutInflater.from(parent.getContext()) - .inflate(R.layout.message, parent, false)); - } - - @Override - protected void onBindViewHolder(@NonNull ChatHolder holder, int position, @NonNull Chat model) { - holder.bind(model); - } - - @Override - public void onDataChanged() { - // If there are no chat messages, show a view that invites the user to add a message. - mBinding.emptyTextView.setVisibility(getItemCount() == 0 ? View.VISIBLE : View.GONE); - } - }; - } - - private void onAddMessage(@NonNull Chat chat) { - sChatCollection.add(chat).addOnFailureListener(this, - e -> Log.e(TAG, "Failed to write message", e)); - } -} diff --git a/app/src/main/java/com/firebase/uidemo/database/firestore/FirestorePagingActivity.java b/app/src/main/java/com/firebase/uidemo/database/firestore/FirestorePagingActivity.java deleted file mode 100644 index 6563ef410..000000000 --- a/app/src/main/java/com/firebase/uidemo/database/firestore/FirestorePagingActivity.java +++ /dev/null @@ -1,200 +0,0 @@ -package com.firebase.uidemo.database.firestore; - -import android.os.Bundle; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; -import android.widget.Toast; - -import com.firebase.ui.firestore.paging.FirestorePagingAdapter; -import com.firebase.ui.firestore.paging.FirestorePagingOptions; -import com.firebase.uidemo.R; -import com.firebase.uidemo.databinding.ActivityFirestorePagingBinding; -import com.google.android.gms.tasks.OnCompleteListener; -import com.google.android.gms.tasks.Task; -import com.google.firebase.firestore.CollectionReference; -import com.google.firebase.firestore.FirebaseFirestore; -import com.google.firebase.firestore.Query; -import com.google.firebase.firestore.WriteBatch; - -import java.util.Locale; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import androidx.paging.CombinedLoadStates; -import androidx.paging.LoadState; -import androidx.paging.PagingConfig; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; -import kotlin.Unit; -import kotlin.jvm.functions.Function1; - -public class FirestorePagingActivity extends AppCompatActivity { - - private static final String TAG = "FirestorePagingActivity"; - - private ActivityFirestorePagingBinding mBinding; - - private FirebaseFirestore mFirestore; - private CollectionReference mItemsCollection; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mBinding = ActivityFirestorePagingBinding.inflate(getLayoutInflater()); - setContentView(mBinding.getRoot()); - - mFirestore = FirebaseFirestore.getInstance(); - mItemsCollection = mFirestore.collection("items"); - - setUpAdapter(); - } - - private void setUpAdapter() { - Query baseQuery = mItemsCollection.orderBy("value", Query.Direction.ASCENDING); - - PagingConfig config = new PagingConfig(20, 10, false); - - FirestorePagingOptions options = new FirestorePagingOptions.Builder() - .setLifecycleOwner(this) - .setQuery(baseQuery, config, Item.class) - .build(); - - final FirestorePagingAdapter adapter = - new FirestorePagingAdapter(options) { - @NonNull - @Override - public ItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, - int viewType) { - View view = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.item_item, parent, false); - return new ItemViewHolder(view); - } - - @Override - protected void onBindViewHolder(@NonNull ItemViewHolder holder, - int position, - @NonNull Item model) { - holder.bind(model); - } - }; - adapter.addLoadStateListener(states -> { - LoadState refresh = states.getRefresh(); - LoadState append = states.getAppend(); - - if (refresh instanceof LoadState.Error || append instanceof LoadState.Error) { - showToast("An error occurred."); - adapter.retry(); - } - - if (append instanceof LoadState.Loading) { - mBinding.swipeRefreshLayout.setRefreshing(true); - } - - if (append instanceof LoadState.NotLoading) { - LoadState.NotLoading notLoading = (LoadState.NotLoading) append; - if (notLoading.getEndOfPaginationReached()) { - // This indicates that the user has scrolled - // until the end of the data set. - mBinding.swipeRefreshLayout.setRefreshing(false); - showToast("Reached end of data set."); - return null; - } - - if (refresh instanceof LoadState.NotLoading) { - // This indicates the most recent load - // has finished. - mBinding.swipeRefreshLayout.setRefreshing(false); - return null; - } - } - return null; - }); - - mBinding.pagingRecycler.setLayoutManager(new LinearLayoutManager(this)); - mBinding.pagingRecycler.setAdapter(adapter); - - mBinding.swipeRefreshLayout.setOnRefreshListener(() -> adapter.refresh()); - } - - @Override - public boolean onCreateOptionsMenu(@NonNull Menu menu) { - getMenuInflater().inflate(R.menu.menu_paging, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - if (item.getItemId() == R.id.item_add_data) { - showToast("Adding data..."); - createItems().addOnCompleteListener(this, task -> { - if (task.isSuccessful()) { - showToast("Data added."); - } else { - Log.w(TAG, "addData", task.getException()); - showToast("Error adding data."); - } - }); - - return true; - } - return super.onOptionsItemSelected(item); - } - - @NonNull - private Task createItems() { - WriteBatch writeBatch = mFirestore.batch(); - - for (int i = 0; i < 250; i++) { - String title = "Item " + i; - - String id = String.format(Locale.getDefault(), "item_%03d", i); - Item item = new Item(title, i); - - writeBatch.set(mItemsCollection.document(id), item); - } - - return writeBatch.commit(); - } - - private void showToast(@NonNull String message) { - Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); - } - - public static class Item { - - @Nullable public String text; - public int value; - - public Item() {} - - public Item(@Nullable String text, int value) { - this.text = text; - this.value = value; - } - - } - - public static class ItemViewHolder extends RecyclerView.ViewHolder { - TextView mTextView; - TextView mValueView; - - ItemViewHolder(@NonNull View itemView) { - super(itemView); - mTextView = itemView.findViewById(R.id.item_text); - mValueView = itemView.findViewById(R.id.item_value); - } - - void bind(@NonNull Item item) { - mTextView.setText(item.text); - mValueView.setText(String.valueOf(item.value)); - } - } - -} diff --git a/app/src/main/java/com/firebase/uidemo/database/realtime/Chat.java b/app/src/main/java/com/firebase/uidemo/database/realtime/Chat.java deleted file mode 100644 index 55361adbc..000000000 --- a/app/src/main/java/com/firebase/uidemo/database/realtime/Chat.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.firebase.uidemo.database.realtime; - -import com.firebase.uidemo.database.AbstractChat; -import com.google.firebase.database.IgnoreExtraProperties; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -@IgnoreExtraProperties -public class Chat extends AbstractChat { - private String mName; - private String mMessage; - private String mUid; - - public Chat() { - // Needed for Firebase - } - - public Chat(@Nullable String name, @Nullable String message, @NonNull String uid) { - mName = name; - mMessage = message; - mUid = uid; - } - - @Override - @Nullable - public String getName() { - return mName; - } - - public void setName(@Nullable String name) { - mName = name; - } - - @Override - @Nullable - public String getMessage() { - return mMessage; - } - - public void setMessage(@Nullable String message) { - mMessage = message; - } - - @Override - @NonNull - public String getUid() { - return mUid; - } - - public void setUid(@NonNull String uid) { - mUid = uid; - } - - @Override - public boolean equals(@Nullable Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Chat chat = (Chat) o; - - return mUid.equals(chat.mUid) - && (mName == null ? chat.mName == null : mName.equals(chat.mName)) - && (mMessage == null ? chat.mMessage == null : mMessage.equals(chat.mMessage)); - } - - @Override - public int hashCode() { - int result = mName == null ? 0 : mName.hashCode(); - result = 31 * result + (mMessage == null ? 0 : mMessage.hashCode()); - result = 31 * result + mUid.hashCode(); - return result; - } - - @Override - @NonNull - public String toString() { - return "Chat{" + - "mName='" + mName + '\'' + - ", mMessage='" + mMessage + '\'' + - ", mUid='" + mUid + '\'' + - '}'; - } -} diff --git a/app/src/main/java/com/firebase/uidemo/database/realtime/FirebaseDbPagingActivity.java b/app/src/main/java/com/firebase/uidemo/database/realtime/FirebaseDbPagingActivity.java deleted file mode 100644 index 7c6aab3da..000000000 --- a/app/src/main/java/com/firebase/uidemo/database/realtime/FirebaseDbPagingActivity.java +++ /dev/null @@ -1,197 +0,0 @@ -package com.firebase.uidemo.database.realtime; - -import android.os.Bundle; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.TextView; -import android.widget.Toast; - -import com.firebase.ui.database.paging.DatabasePagingOptions; -import com.firebase.ui.database.paging.FirebaseRecyclerPagingAdapter; -import com.firebase.uidemo.R; -import com.firebase.uidemo.databinding.ActivityDatabasePagingBinding; -import com.google.firebase.database.FirebaseDatabase; -import com.google.firebase.database.Query; - -import java.util.Locale; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import androidx.paging.LoadState; -import androidx.paging.PagingConfig; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; - -public class FirebaseDbPagingActivity extends AppCompatActivity { - - private static final String TAG = "PagingActivity"; - - private ActivityDatabasePagingBinding mBinding; - - private FirebaseDatabase mDatabase; - private Query mQuery; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mBinding = ActivityDatabasePagingBinding.inflate(getLayoutInflater()); - setContentView(mBinding.getRoot()); - - mDatabase = FirebaseDatabase.getInstance(); - mQuery = mDatabase.getReference().child("items"); - - setUpAdapter(); - } - - private void setUpAdapter() { - - //Initialize Paging Configurations - PagingConfig config = new PagingConfig(30, 5, false); - - //Initialize Firebase Paging Options - DatabasePagingOptions options = new DatabasePagingOptions.Builder() - .setLifecycleOwner(this) - .setQuery(mQuery, config, Item.class) - .build(); - - //Initializing Adapter - final FirebaseRecyclerPagingAdapter mAdapter = - new FirebaseRecyclerPagingAdapter(options) { - @NonNull - @Override - public ItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, - int viewType) { - View view = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.item_item, parent, false); - return new ItemViewHolder(view); - } - - @Override - protected void onBindViewHolder(@NonNull ItemViewHolder holder, - int position, - @NonNull Item model) { - holder.bind(model); - } - }; - - mAdapter.addLoadStateListener(states -> { - LoadState refresh = states.getRefresh(); - LoadState append = states.getAppend(); - - if (refresh instanceof LoadState.Error) { - LoadState.Error loadStateError = (LoadState.Error) refresh; - mBinding.swipeRefreshLayout.setRefreshing(false); - Log.e(TAG, loadStateError.getError().getLocalizedMessage()); - } - if (append instanceof LoadState.Error) { - LoadState.Error loadStateError = (LoadState.Error) append; - mBinding.swipeRefreshLayout.setRefreshing(false); - Log.e(TAG, loadStateError.getError().getLocalizedMessage()); - } - - if (append instanceof LoadState.Loading) { - mBinding.swipeRefreshLayout.setRefreshing(true); - } - - if (append instanceof LoadState.NotLoading) { - LoadState.NotLoading notLoading = (LoadState.NotLoading) append; - if (notLoading.getEndOfPaginationReached()) { - // This indicates that the user has scrolled - // until the end of the data set. - mBinding.swipeRefreshLayout.setRefreshing(false); - showToast("Reached end of data set."); - return null; - } - - if (refresh instanceof LoadState.NotLoading) { - // This indicates the most recent load - // has finished. - mBinding.swipeRefreshLayout.setRefreshing(false); - return null; - } - } - return null; - }); - - mBinding.pagingRecycler.setLayoutManager(new LinearLayoutManager(this)); - mBinding.pagingRecycler.setAdapter(mAdapter); - - // Reload data on swipe - mBinding.swipeRefreshLayout.setOnRefreshListener(() -> { - //Reload Data - mAdapter.refresh(); - }); - } - - - @Override - public boolean onCreateOptionsMenu(@NonNull Menu menu) { - getMenuInflater().inflate(R.menu.menu_paging, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - if (item.getItemId() == R.id.item_add_data) { - showToast("Adding data..."); - createItems(); - return true; - } - return super.onOptionsItemSelected(item); - } - - @NonNull - private void createItems() { - for (int i = 0; i < 250; i++) { - String title = "Item " + i; - - String id = String.format(Locale.getDefault(), "item_%03d", i); - Item item = new Item(title, i); - - mDatabase.getReference("items").child(id).setValue(item); - } - } - - private void showToast(@NonNull String message) { - Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); - } - - - public static class Item { - - @Nullable - public String text; - public int value; - - public Item(){} - - public Item(@Nullable String text, int value) { - this.text = text; - this.value = value; - } - } - - public static class ItemViewHolder extends RecyclerView.ViewHolder { - - TextView mTextView; - TextView mValueView; - - ItemViewHolder(@NonNull View itemView) { - super(itemView); - mTextView = itemView.findViewById(R.id.item_text); - mValueView = itemView.findViewById(R.id.item_value); - } - - void bind(@NonNull Item item) { - mTextView.setText(item.text); - mValueView.setText(String.valueOf(item.value)); - } - } - -} diff --git a/app/src/main/java/com/firebase/uidemo/database/realtime/RealtimeDbChatActivity.java b/app/src/main/java/com/firebase/uidemo/database/realtime/RealtimeDbChatActivity.java deleted file mode 100644 index 381893f7c..000000000 --- a/app/src/main/java/com/firebase/uidemo/database/realtime/RealtimeDbChatActivity.java +++ /dev/null @@ -1,156 +0,0 @@ -package com.firebase.uidemo.database.realtime; - -import android.annotation.SuppressLint; -import android.os.Bundle; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Toast; - -import com.firebase.ui.auth.util.ui.ImeHelper; -import com.firebase.ui.database.FirebaseRecyclerAdapter; -import com.firebase.ui.database.FirebaseRecyclerOptions; -import com.firebase.uidemo.R; -import com.firebase.uidemo.database.ChatHolder; -import com.firebase.uidemo.databinding.ActivityChatBinding; -import com.firebase.uidemo.util.SignInResultNotifier; -import com.google.firebase.auth.FirebaseAuth; -import com.google.firebase.database.DatabaseError; -import com.google.firebase.database.DatabaseReference; -import com.google.firebase.database.FirebaseDatabase; -import com.google.firebase.database.Query; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - -/** - * Class demonstrating how to setup a {@link RecyclerView} with an adapter while taking sign-in - * states into consideration. Also demonstrates adding data to a ref and then reading it back using - * the {@link FirebaseRecyclerAdapter} to build a simple chat app. - *

- * For a general intro to the RecyclerView, see Creating - * Lists. - */ -@SuppressLint("RestrictedApi") -public class RealtimeDbChatActivity extends AppCompatActivity - implements FirebaseAuth.AuthStateListener { - private static final String TAG = "RealtimeDatabaseDemo"; - - /** - * Get the last 50 chat messages. - */ - @NonNull - protected final Query sChatQuery = - FirebaseDatabase.getInstance().getReference().child("chats").limitToLast(50); - - private ActivityChatBinding mBinding; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mBinding = ActivityChatBinding.inflate(getLayoutInflater()); - setContentView(mBinding.getRoot()); - - mBinding.messagesList.setHasFixedSize(true); - mBinding.messagesList.setLayoutManager(new LinearLayoutManager(this)); - - ImeHelper.setImeOnDoneListener(mBinding.messageEdit, () -> onSendClick()); - - mBinding.sendButton.setOnClickListener(view -> onSendClick()); - } - - @Override - public void onStart() { - super.onStart(); - if (isSignedIn()) { - attachRecyclerViewAdapter(); - } - FirebaseAuth.getInstance().addAuthStateListener(this); - } - - @Override - protected void onStop() { - super.onStop(); - FirebaseAuth.getInstance().removeAuthStateListener(this); - } - - @Override - public void onAuthStateChanged(@NonNull FirebaseAuth auth) { - mBinding.sendButton.setEnabled(isSignedIn()); - mBinding.messageEdit.setEnabled(isSignedIn()); - - if (isSignedIn()) { - attachRecyclerViewAdapter(); - } else { - Toast.makeText(this, R.string.signing_in, Toast.LENGTH_SHORT).show(); - auth.signInAnonymously().addOnCompleteListener(new SignInResultNotifier(this)); - } - } - - private boolean isSignedIn() { - return FirebaseAuth.getInstance().getCurrentUser() != null; - } - - private void attachRecyclerViewAdapter() { - final RecyclerView.Adapter adapter = newAdapter(); - - // Scroll to bottom on new messages - adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { - @Override - public void onItemRangeInserted(int positionStart, int itemCount) { - mBinding.messagesList.smoothScrollToPosition(adapter.getItemCount()); - } - }); - - mBinding.messagesList.setAdapter(adapter); - } - - public void onSendClick() { - String uid = FirebaseAuth.getInstance().getCurrentUser().getUid(); - String name = "User " + uid.substring(0, 6); - - onAddMessage(new Chat(name, mBinding.messageEdit.getText().toString(), uid)); - - mBinding.messageEdit.setText(""); - } - - @NonNull - protected RecyclerView.Adapter newAdapter() { - FirebaseRecyclerOptions options = - new FirebaseRecyclerOptions.Builder() - .setQuery(sChatQuery, Chat.class) - .setLifecycleOwner(this) - .build(); - - return new FirebaseRecyclerAdapter(options) { - @Override - public ChatHolder onCreateViewHolder(ViewGroup parent, int viewType) { - return new ChatHolder(LayoutInflater.from(parent.getContext()) - .inflate(R.layout.message, parent, false)); - } - - @Override - protected void onBindViewHolder(@NonNull ChatHolder holder, int position, @NonNull Chat model) { - holder.bind(model); - } - - @Override - public void onDataChanged() { - // If there are no chat messages, show a view that invites the user to add a message. - mBinding.emptyTextView.setVisibility(getItemCount() == 0 ? View.VISIBLE : View.GONE); - } - }; - } - - protected void onAddMessage(@NonNull Chat chat) { - sChatQuery.getRef().push().setValue(chat, (error, reference) -> { - if (error != null) { - Log.e(TAG, "Failed to write message", error.toException()); - } - }); - } -} diff --git a/app/src/main/java/com/firebase/uidemo/database/realtime/RealtimeDbChatIndexActivity.java b/app/src/main/java/com/firebase/uidemo/database/realtime/RealtimeDbChatIndexActivity.java deleted file mode 100644 index c55b2cd24..000000000 --- a/app/src/main/java/com/firebase/uidemo/database/realtime/RealtimeDbChatIndexActivity.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.firebase.uidemo.database.realtime; - -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.firebase.uidemo.R; -import com.firebase.uidemo.database.ChatHolder; -import com.google.firebase.auth.FirebaseAuth; -import com.google.firebase.database.DatabaseReference; -import com.google.firebase.database.FirebaseDatabase; - -import androidx.annotation.NonNull; - -public class RealtimeDbChatIndexActivity extends RealtimeDbChatActivity { - private DatabaseReference mChatIndicesRef; - - @NonNull - @Override - protected FirebaseRecyclerAdapter newAdapter() { - mChatIndicesRef = FirebaseDatabase.getInstance() - .getReference() - .child("chatIndices") - .child(FirebaseAuth.getInstance().getCurrentUser().getUid()); - - FirebaseRecyclerOptions options = - new FirebaseRecyclerOptions.Builder() - .setIndexedQuery( - mChatIndicesRef.limitToFirst(50), sChatQuery.getRef(), Chat.class) - .setLifecycleOwner(this) - .build(); - - return new FirebaseRecyclerAdapter(options) { - @NonNull - @Override - public ChatHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - return new ChatHolder(LayoutInflater.from(parent.getContext()) - .inflate(R.layout.message, parent, false)); - } - - @Override - protected void onBindViewHolder(@NonNull ChatHolder holder, int position, @NonNull Chat model) { - holder.bind(model); - } - - @Override - public void onDataChanged() { - // If there are no chat messages, show a view that invites the user to add a message. - findViewById(R.id.emptyTextView).setVisibility(getItemCount() == 0 ? View.VISIBLE : View.GONE); - } - }; - } - - @Override - protected void onAddMessage(@NonNull Chat chat) { - DatabaseReference chatRef = sChatQuery.getRef().push(); - mChatIndicesRef.child(chatRef.getKey()).setValue(true); - chatRef.setValue(chat); - } -} diff --git a/app/src/main/java/com/firebase/uidemo/storage/ImageActivity.java b/app/src/main/java/com/firebase/uidemo/storage/ImageActivity.java deleted file mode 100644 index bf2f57a80..000000000 --- a/app/src/main/java/com/firebase/uidemo/storage/ImageActivity.java +++ /dev/null @@ -1,160 +0,0 @@ -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.util.Log; -import android.view.View; -import android.widget.Toast; - -import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions; -import com.firebase.uidemo.BuildConfig; -import com.firebase.uidemo.R; -import com.firebase.uidemo.databinding.ActivityImageBinding; -import com.firebase.uidemo.util.SignInResultNotifier; -import com.google.android.gms.tasks.OnFailureListener; -import com.google.android.gms.tasks.OnSuccessListener; -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.Collections; -import java.util.List; -import java.util.UUID; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import pub.devrel.easypermissions.AfterPermissionGranted; -import pub.devrel.easypermissions.AppSettingsDialog; -import pub.devrel.easypermissions.EasyPermissions; - -public class ImageActivity extends AppCompatActivity implements EasyPermissions.PermissionCallbacks { - - private static final String TAG = "ImageDemo"; - private static final int RC_CHOOSE_PHOTO = 101; - private static final int RC_IMAGE_PERMS = 102; - private static final String PERMS = Manifest.permission.READ_EXTERNAL_STORAGE; - - private StorageReference mImageRef; - - private ActivityImageBinding mBinding; - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - mBinding = ActivityImageBinding.inflate(getLayoutInflater()); - setContentView(mBinding.getRoot()); - - mBinding.buttonDownloadDirect.setOnClickListener(view -> { - // Download directly from StorageReference using Glide - // (See MyAppGlideModule for Loader registration) - GlideApp.with(ImageActivity.this) - .load(mImageRef) - .centerCrop() - .transition(DrawableTransitionOptions.withCrossFade()) - .into(mBinding.firstImage); - }); - - mBinding.buttonChoosePhoto.setOnClickListener(view -> choosePhoto()); - - // By default, Cloud 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 SignInResultNotifier(this)); - } - - @Override - public void onActivityResult(int requestCode, int resultCode, @Nullable 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(); - } - } else if (requestCode == AppSettingsDialog.DEFAULT_SETTINGS_REQ_CODE - && EasyPermissions.hasPermissions(this, PERMS)) { - choosePhoto(); - } - } - - @AfterPermissionGranted(RC_IMAGE_PERMS) - protected void choosePhoto() { - if (!EasyPermissions.hasPermissions(this, PERMS)) { - EasyPermissions.requestPermissions(this, getString(R.string.rational_image_perm), - RC_IMAGE_PERMS, PERMS); - return; - } - - Intent i = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); - startActivityForResult(i, RC_CHOOSE_PHOTO); - } - - private void uploadPhoto(Uri uri) { - // Reset UI - hideDownloadUI(); - Toast.makeText(this, "Uploading...", Toast.LENGTH_SHORT).show(); - - // Upload to Cloud Storage - String uuid = UUID.randomUUID().toString(); - mImageRef = FirebaseStorage.getInstance().getReference(uuid); - mImageRef.putFile(uri) - .addOnSuccessListener(this, taskSnapshot -> { - if (BuildConfig.DEBUG) { - Log.d(TAG, "uploadPhoto:onSuccess:" + - taskSnapshot.getMetadata().getReference().getPath()); - } - Toast.makeText(ImageActivity.this, "Image uploaded", - Toast.LENGTH_SHORT).show(); - - showDownloadUI(); - }) - .addOnFailureListener(this, e -> { - Log.w(TAG, "uploadPhoto:onError", e); - Toast.makeText(ImageActivity.this, "Upload failed", - Toast.LENGTH_SHORT).show(); - }); - } - - private void hideDownloadUI() { - mBinding.buttonDownloadDirect.setEnabled(false); - - mBinding.firstImage.setImageResource(0); - mBinding.firstImage.setVisibility(View.INVISIBLE); - } - - private void showDownloadUI() { - mBinding.buttonDownloadDirect.setEnabled(true); - - mBinding.firstImage.setVisibility(View.VISIBLE); - } - - @Override - public void onRequestPermissionsResult(int requestCode, - @NonNull String[] permissions, - @NonNull int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this); - } - - @Override - public void onPermissionsGranted(int requestCode, @NonNull List perms) { - // See #choosePhoto with @AfterPermissionGranted - } - - @Override - public void onPermissionsDenied(int requestCode, @NonNull List perms) { - if (EasyPermissions.somePermissionPermanentlyDenied(this, - Collections.singletonList(PERMS))) { - new AppSettingsDialog.Builder(this).build().show(); - } - } -} diff --git a/app/src/main/java/com/firebase/uidemo/storage/MyAppGlideModule.java b/app/src/main/java/com/firebase/uidemo/storage/MyAppGlideModule.java deleted file mode 100644 index bc1f11568..000000000 --- a/app/src/main/java/com/firebase/uidemo/storage/MyAppGlideModule.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.firebase.uidemo.storage; - -import android.content.Context; - -import com.bumptech.glide.Glide; -import com.bumptech.glide.Registry; -import com.bumptech.glide.annotation.GlideModule; -import com.bumptech.glide.module.AppGlideModule; -import com.firebase.ui.storage.images.FirebaseImageLoader; -import com.google.firebase.storage.StorageReference; - -import java.io.InputStream; - -import androidx.annotation.NonNull; - -/** - * Glide module to register {@link com.firebase.ui.storage.images.FirebaseImageLoader}. - * See: http://bumptech.github.io/glide/doc/generatedapi.html - */ -@GlideModule -public class MyAppGlideModule extends AppGlideModule { - - @Override - public void registerComponents(@NonNull Context context, - @NonNull Glide glide, - @NonNull Registry registry) { - // Register FirebaseImageLoader to handle StorageReference - registry.append(StorageReference.class, InputStream.class, - new FirebaseImageLoader.Factory()); - } - -} diff --git a/composeapp/src/main/java/com/firebase/composeapp/ui/theme/Color.kt b/app/src/main/java/com/firebase/uidemo/ui/theme/Color.kt similarity index 85% rename from composeapp/src/main/java/com/firebase/composeapp/ui/theme/Color.kt rename to app/src/main/java/com/firebase/uidemo/ui/theme/Color.kt index 994915613..ead7d1c46 100644 --- a/composeapp/src/main/java/com/firebase/composeapp/ui/theme/Color.kt +++ b/app/src/main/java/com/firebase/uidemo/ui/theme/Color.kt @@ -1,4 +1,4 @@ -package com.firebase.composeapp.ui.theme +package com.firebase.uidemo.ui.theme import androidx.compose.ui.graphics.Color diff --git a/composeapp/src/main/java/com/firebase/composeapp/ui/theme/Theme.kt b/app/src/main/java/com/firebase/uidemo/ui/theme/Theme.kt similarity index 97% rename from composeapp/src/main/java/com/firebase/composeapp/ui/theme/Theme.kt rename to app/src/main/java/com/firebase/uidemo/ui/theme/Theme.kt index 08bd9441a..4d34bc26d 100644 --- a/composeapp/src/main/java/com/firebase/composeapp/ui/theme/Theme.kt +++ b/app/src/main/java/com/firebase/uidemo/ui/theme/Theme.kt @@ -1,4 +1,4 @@ -package com.firebase.composeapp.ui.theme +package com.firebase.uidemo.ui.theme import android.app.Activity import android.os.Build diff --git a/composeapp/src/main/java/com/firebase/composeapp/ui/theme/Type.kt b/app/src/main/java/com/firebase/uidemo/ui/theme/Type.kt similarity index 95% rename from composeapp/src/main/java/com/firebase/composeapp/ui/theme/Type.kt rename to app/src/main/java/com/firebase/uidemo/ui/theme/Type.kt index ea0657654..f93d7a7ce 100644 --- a/composeapp/src/main/java/com/firebase/composeapp/ui/theme/Type.kt +++ b/app/src/main/java/com/firebase/uidemo/ui/theme/Type.kt @@ -1,4 +1,4 @@ -package com.firebase.composeapp.ui.theme +package com.firebase.uidemo.ui.theme import androidx.compose.material3.Typography import androidx.compose.ui.text.TextStyle diff --git a/app/src/main/java/com/firebase/uidemo/util/ConfigurationUtils.java b/app/src/main/java/com/firebase/uidemo/util/ConfigurationUtils.java deleted file mode 100644 index bb7545bdf..000000000 --- a/app/src/main/java/com/firebase/uidemo/util/ConfigurationUtils.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.firebase.uidemo.util; - -import android.annotation.SuppressLint; -import android.content.Context; - -import com.firebase.ui.auth.AuthUI; -import com.firebase.uidemo.R; -import com.google.firebase.auth.ActionCodeSettings; - -import java.util.ArrayList; -import java.util.List; - -import androidx.annotation.NonNull; - -@SuppressLint("RestrictedApi") -public final class ConfigurationUtils { - - private ConfigurationUtils() { - throw new AssertionError("No instance for you!"); - } - - public static boolean isGoogleMisconfigured(@NonNull Context context) { - return AuthUI.UNCONFIGURED_CONFIG_VALUE.equals( - context.getString(R.string.default_web_client_id)); - } - - public static boolean isFacebookMisconfigured(@NonNull Context context) { - return AuthUI.UNCONFIGURED_CONFIG_VALUE.equals( - context.getString(R.string.facebook_application_id)); - } - - @NonNull - public static List getConfiguredProviders(@NonNull Context context) { - List providers = new ArrayList<>(); - - if (!isGoogleMisconfigured(context)) { - providers.add(new AuthUI.IdpConfig.GoogleBuilder().build()); - } - - if (!isFacebookMisconfigured(context)) { - providers.add(new AuthUI.IdpConfig.FacebookBuilder().build()); - } - - ActionCodeSettings actionCodeSettings = ActionCodeSettings.newBuilder() - .setAndroidPackageName("com.firebase.uidemo", true, null) - .setHandleCodeInApp(true) - .setUrl("https://google.com") - .build(); - - providers.add(new AuthUI.IdpConfig.EmailBuilder() - .setAllowNewAccounts(true) - .enableEmailLinkSignIn() - .setActionCodeSettings(actionCodeSettings) - .build()); - - providers.add(new AuthUI.IdpConfig.TwitterBuilder().build()); - providers.add(new AuthUI.IdpConfig.PhoneBuilder().build()); - providers.add(new AuthUI.IdpConfig.MicrosoftBuilder().build()); - providers.add(new AuthUI.IdpConfig.YahooBuilder().build()); - providers.add(new AuthUI.IdpConfig.AppleBuilder().build()); - - return providers; - } -} diff --git a/app/src/main/java/com/firebase/uidemo/util/SignInResultNotifier.java b/app/src/main/java/com/firebase/uidemo/util/SignInResultNotifier.java deleted file mode 100644 index ec3d96f0e..000000000 --- a/app/src/main/java/com/firebase/uidemo/util/SignInResultNotifier.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.firebase.uidemo.util; - -import android.content.Context; -import android.widget.Toast; - -import com.firebase.uidemo.R; -import com.google.android.gms.tasks.OnCompleteListener; -import com.google.android.gms.tasks.Task; -import com.google.firebase.auth.AuthResult; - -import androidx.annotation.NonNull; - -/** - * Notifies the user of sign in successes or failures beyond the lifecycle of an activity. - */ -public class SignInResultNotifier implements OnCompleteListener { - private Context mContext; - - public SignInResultNotifier(@NonNull Context context) { - mContext = context.getApplicationContext(); - } - - @Override - public void onComplete(@NonNull Task task) { - if (task.isSuccessful()) { - Toast.makeText(mContext, R.string.signed_in, Toast.LENGTH_SHORT).show(); - } else { - Toast.makeText(mContext, R.string.anonymous_auth_failed_msg, Toast.LENGTH_LONG).show(); - } - } -} diff --git a/composeapp/src/main/res/drawable-hdpi/firebase_auth.png b/app/src/main/res/drawable-hdpi/firebase_auth.png similarity index 100% rename from composeapp/src/main/res/drawable-hdpi/firebase_auth.png rename to app/src/main/res/drawable-hdpi/firebase_auth.png diff --git a/app/src/main/res/drawable-hdpi/firebase_auth_120dp.png b/app/src/main/res/drawable-hdpi/firebase_auth_120dp.png deleted file mode 100644 index b03b18b53..000000000 Binary files a/app/src/main/res/drawable-hdpi/firebase_auth_120dp.png and /dev/null differ diff --git a/composeapp/src/main/res/drawable-mdpi/firebase_auth.png b/app/src/main/res/drawable-mdpi/firebase_auth.png similarity index 100% rename from composeapp/src/main/res/drawable-mdpi/firebase_auth.png rename to app/src/main/res/drawable-mdpi/firebase_auth.png diff --git a/app/src/main/res/drawable-mdpi/firebase_auth_120dp.png b/app/src/main/res/drawable-mdpi/firebase_auth_120dp.png deleted file mode 100644 index 820ba76f9..000000000 Binary files a/app/src/main/res/drawable-mdpi/firebase_auth_120dp.png and /dev/null differ diff --git a/composeapp/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml similarity index 100% rename from composeapp/src/main/res/drawable-v24/ic_launcher_foreground.xml rename to app/src/main/res/drawable-v24/ic_launcher_foreground.xml diff --git a/composeapp/src/main/res/drawable-xhdpi/firebase_auth.png b/app/src/main/res/drawable-xhdpi/firebase_auth.png similarity index 100% rename from composeapp/src/main/res/drawable-xhdpi/firebase_auth.png rename to app/src/main/res/drawable-xhdpi/firebase_auth.png diff --git a/app/src/main/res/drawable-xhdpi/firebase_auth_120dp.png b/app/src/main/res/drawable-xhdpi/firebase_auth_120dp.png deleted file mode 100644 index 351fa59fe..000000000 Binary files a/app/src/main/res/drawable-xhdpi/firebase_auth_120dp.png and /dev/null differ diff --git a/composeapp/src/main/res/drawable-xxhdpi/firebase_auth.png b/app/src/main/res/drawable-xxhdpi/firebase_auth.png similarity index 100% rename from composeapp/src/main/res/drawable-xxhdpi/firebase_auth.png rename to app/src/main/res/drawable-xxhdpi/firebase_auth.png diff --git a/app/src/main/res/drawable-xxhdpi/firebase_auth_120dp.png b/app/src/main/res/drawable-xxhdpi/firebase_auth_120dp.png deleted file mode 100644 index 6a6374e30..000000000 Binary files a/app/src/main/res/drawable-xxhdpi/firebase_auth_120dp.png and /dev/null differ diff --git a/composeapp/src/main/res/drawable-xxxhdpi/firebase_auth.png b/app/src/main/res/drawable-xxxhdpi/firebase_auth.png similarity index 100% rename from composeapp/src/main/res/drawable-xxxhdpi/firebase_auth.png rename to app/src/main/res/drawable-xxxhdpi/firebase_auth.png diff --git a/app/src/main/res/drawable-xxxhdpi/firebase_auth_120dp.png b/app/src/main/res/drawable-xxxhdpi/firebase_auth_120dp.png deleted file mode 100644 index 9b68e8ade..000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/firebase_auth_120dp.png and /dev/null differ diff --git a/app/src/main/res/drawable/custom_bg_gradient.xml b/app/src/main/res/drawable/custom_bg_gradient.xml deleted file mode 100644 index 58479ea3c..000000000 --- a/app/src/main/res/drawable/custom_bg_gradient.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/app/src/main/res/drawable/ic_anon_user_48dp.xml b/app/src/main/res/drawable/ic_anon_user_48dp.xml deleted file mode 100644 index 0fb12013f..000000000 --- a/app/src/main/res/drawable/ic_anon_user_48dp.xml +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/ic_chat_message_arrow.xml b/app/src/main/res/drawable/ic_chat_message_arrow.xml deleted file mode 100644 index 5063dc73e..000000000 --- a/app/src/main/res/drawable/ic_chat_message_arrow.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - diff --git a/app/src/main/res/drawable/ic_chat_message_background.xml b/app/src/main/res/drawable/ic_chat_message_background.xml deleted file mode 100644 index c22c3d71a..000000000 --- a/app/src/main/res/drawable/ic_chat_message_background.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/composeapp/src/main/res/drawable/ic_discord_24dp.xml b/app/src/main/res/drawable/ic_discord_24dp.xml similarity index 100% rename from composeapp/src/main/res/drawable/ic_discord_24dp.xml rename to app/src/main/res/drawable/ic_discord_24dp.xml diff --git a/app/src/main/res/drawable/ic_googleg_color_144dp.xml b/app/src/main/res/drawable/ic_googleg_color_144dp.xml deleted file mode 100644 index 820e04185..000000000 --- a/app/src/main/res/drawable/ic_googleg_color_144dp.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - diff --git a/composeapp/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml similarity index 100% rename from composeapp/src/main/res/drawable/ic_launcher_background.xml rename to app/src/main/res/drawable/ic_launcher_background.xml diff --git a/composeapp/src/main/res/drawable/ic_line_logo_24dp.xml b/app/src/main/res/drawable/ic_line_logo_24dp.xml similarity index 100% rename from composeapp/src/main/res/drawable/ic_line_logo_24dp.xml rename to app/src/main/res/drawable/ic_line_logo_24dp.xml diff --git a/app/src/main/res/layout-land/auth_method_picker_custom_layout.xml b/app/src/main/res/layout-land/auth_method_picker_custom_layout.xml deleted file mode 100644 index 52a234d53..000000000 --- a/app/src/main/res/layout-land/auth_method_picker_custom_layout.xml +++ /dev/null @@ -1,190 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/activity_anonymous_upgrade.xml b/app/src/main/res/layout/activity_anonymous_upgrade.xml deleted file mode 100644 index 2677a1c71..000000000 --- a/app/src/main/res/layout/activity_anonymous_upgrade.xml +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - -