├── .DS_Store
├── .gitignore
├── LICENSE
├── README.md
├── WalkthroughActivity.iml
├── app
├── .DS_Store
├── .gitignore
├── app.iml
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── instamobile
│ │ └── ui
│ │ └── fragment
│ │ └── onBoarding
│ │ └── walkthroughactivity
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── instamobile
│ │ │ └── firebaseStarterKit
│ │ │ ├── adapter
│ │ │ ├── BindingAdapter.kt
│ │ │ └── SliderAdapter.kt
│ │ │ ├── model
│ │ │ ├── SlideContent.kt
│ │ │ └── UserModel.kt
│ │ │ ├── ui
│ │ │ ├── activity
│ │ │ │ └── host
│ │ │ │ │ └── HostActivity.kt
│ │ │ └── fragment
│ │ │ │ ├── authHost
│ │ │ │ ├── AuthFragment.kt
│ │ │ │ └── AuthViewModel.kt
│ │ │ │ ├── home
│ │ │ │ └── HomeFragment.kt
│ │ │ │ ├── login
│ │ │ │ ├── LoginFragment.kt
│ │ │ │ └── LoginViewModel.kt
│ │ │ │ ├── onBoarding
│ │ │ │ ├── OnBoarding.kt
│ │ │ │ └── OnBoardingViewModel.kt
│ │ │ │ └── signUp
│ │ │ │ ├── SignUpFragment.kt
│ │ │ │ └── SignUpViewModel.kt
│ │ │ └── utils
│ │ │ ├── AppLifecycleHandler.kt
│ │ │ ├── FirestoreUtil.kt
│ │ │ ├── MyApplication.kt
│ │ │ ├── Prefs.kt
│ │ │ └── TabLayoutMediator.kt
│ └── res
│ │ ├── drawable-hdpi
│ │ └── instamobile_logo.png
│ │ ├── drawable-mdpi
│ │ └── instamobile_logo.png
│ │ ├── drawable-xhdpi
│ │ └── instamobile_logo.png
│ │ ├── drawable-xxhdpi
│ │ └── instamobile_logo.png
│ │ ├── drawable-xxxhdpi
│ │ └── instamobile_logo.png
│ │ ├── drawable
│ │ ├── btn_background.xml
│ │ ├── disabled_rounded_blue_button.xml
│ │ ├── ic_blue_button.xml
│ │ ├── ic_camera.xml
│ │ ├── ic_chat.xml
│ │ ├── ic_fast.xml
│ │ ├── ic_firebase.xml
│ │ ├── ic_kotlin.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── ic_logout.xml
│ │ ├── ic_save_time.xml
│ │ ├── ic_white_button.xml
│ │ ├── launch_screen.xml
│ │ ├── placeholder.jpg
│ │ ├── rounded_blue_button.xml
│ │ ├── rounded_border.xml
│ │ ├── sign_up_button_selector.xml
│ │ ├── tab_indicator_default.xml
│ │ ├── tab_indicator_selected.xml
│ │ └── tab_selector.xml
│ │ ├── layout
│ │ ├── activity_host.xml
│ │ ├── drawer_header_layout.xml
│ │ ├── fragment_auth.xml
│ │ ├── fragment_home.xml
│ │ ├── fragment_login.xml
│ │ ├── fragment_on_boarding.xml
│ │ ├── fragment_sign_up.xml
│ │ └── slide.xml
│ │ ├── menu
│ │ └── drawer_menu.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── navigation
│ │ └── main_graph.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── instamobile
│ └── ui
│ └── fragment
│ └── onBoarding
│ └── walkthroughactivity
│ └── ExampleUnitTest.kt
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── local.properties
└── settings.gradle
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instamobile/kotlin-firebase/7d4da154cb0981a19d6add5fe13afffa7f3cdbae/.DS_Store
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled class file
2 | *.class
3 |
4 | # Log file
5 | *.log
6 |
7 | # BlueJ files
8 | *.ctxt
9 |
10 | # Mobile Tools for Java (J2ME)
11 | .mtj.tmp/
12 |
13 | # Package Files #
14 | *.jar
15 | *.war
16 | *.nar
17 | *.ear
18 | *.zip
19 | *.tar.gz
20 | *.rar
21 |
22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
23 | hs_err_pid*
24 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Instamobile
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Kotlin Firebase - Android Starter Project
2 |
3 | Android Starter Project with Authentication, Firestore & Storage Integration.
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | This Kotlin project for Android is an open-source boilerplate project with Firebase integrations for:
12 | * User Authentication
13 | * Firestore Database
14 | * Firebase Storage
15 | * Login with Facebook
16 | * Login with Google
17 | * Login with Email & Password
18 | * Profile Photo Upload
19 | * Walkthrough Onboarding Flow
20 |
21 | ## Getting Started
22 | * Clone this repository
23 | * Download google-service.json from your Firebase Console
24 | * Place google-service.json file in the app/ folder
25 | * Build & Run the app in Android Studio
26 |
27 | Save a couple of days of development by bootstrapping your new mobile app with this Kotlin Firebase starter kit for Android.
28 |
29 | Coded with ❤️️ by Instakotlin.
30 |
--------------------------------------------------------------------------------
/WalkthroughActivity.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instamobile/kotlin-firebase/7d4da154cb0981a19d6add5fe13afffa7f3cdbae/app/.DS_Store
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/app.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | generateDebugSources
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | apply plugin: 'kotlin-android'
4 | apply plugin: 'kotlin-kapt'
5 | apply plugin: 'androidx.navigation.safeargs'
6 |
7 | apply plugin: 'kotlin-android-extensions'
8 |
9 | android {
10 | compileSdkVersion 29
11 | buildToolsVersion "29.0.2"
12 | defaultConfig {
13 | applicationId "com.instamobile.firebaseStarterKit"
14 | minSdkVersion 19
15 | targetSdkVersion 29
16 | versionCode 1
17 | multiDexEnabled true
18 | versionName "1.0"
19 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
20 | }
21 | buildTypes {
22 | release {
23 | minifyEnabled false
24 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
25 | }
26 | }
27 | compileOptions {
28 | sourceCompatibility JavaVersion.VERSION_1_8
29 | targetCompatibility JavaVersion.VERSION_1_8
30 | }
31 | kotlinOptions {
32 | jvmTarget = "1.8"
33 | }
34 | dataBinding {
35 | enabled = true
36 | }
37 | }
38 |
39 | dependencies {
40 | implementation fileTree(dir: 'libs', include: ['*.jar'])
41 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
42 | implementation 'androidx.appcompat:appcompat:1.1.0'
43 | implementation 'androidx.core:core-ktx:1.2.0-beta01'
44 | implementation 'com.google.android.material:material:1.0.0'
45 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
46 | implementation 'androidx.legacy:legacy-support-v4:1.0.0'
47 | testImplementation 'junit:junit:4.12'
48 | androidTestImplementation 'androidx.test.ext:junit:1.1.1'
49 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
50 |
51 | //viewpager2 for RTL support
52 | implementation "androidx.viewpager2:viewpager2:1.0.0-rc01"
53 | //databinding
54 | kapt 'androidx.databinding:databinding-compiler:3.5.2'
55 | //multidex
56 | implementation 'androidx.multidex:multidex:2.0.1'
57 |
58 | //nav
59 | implementation 'androidx.navigation:navigation-fragment-ktx:2.2.0-rc01'
60 | implementation 'androidx.navigation:navigation-ui-ktx:2.2.0-rc01'
61 |
62 | //core
63 | implementation 'androidx.core:core-ktx:1.2.0-beta01'
64 |
65 | //view model
66 | implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'
67 | //facebook password-less login
68 | implementation 'com.facebook.android:facebook-android-sdk:5.0.0'
69 | // circleImageView
70 | implementation 'de.hdodenhof:circleimageview:3.0.1'
71 |
72 | //Picasso
73 | implementation 'com.squareup.picasso:picasso:2.71828'
74 |
75 | //firebase
76 | implementation 'com.google.firebase:firebase-core:17.2.1'
77 | implementation 'com.google.firebase:firebase-auth:19.1.0'
78 | implementation 'com.google.firebase:firebase-database:19.2.0'
79 | implementation 'com.google.firebase:firebase-firestore:21.2.1'
80 | implementation 'com.google.firebase:firebase-storage:19.1.0'
81 | implementation 'com.google.firebase:firebase-analytics:17.2.1'
82 | //google auth
83 | implementation 'com.google.android.gms:play-services-auth:17.0.0'
84 |
85 | }
86 | apply plugin: 'com.google.gms.google-services'
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/instamobile/ui/fragment/onBoarding/walkthroughactivity/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.instamobile.ui.fragment.onBoarding.walkthroughactivity
2 |
3 | import androidx.test.ext.junit.runners.AndroidJUnit4
4 | import androidx.test.platform.app.InstrumentationRegistry
5 | import org.junit.Assert.assertEquals
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | /**
10 | * Instrumented test, which will execute on an Android device.
11 | *
12 | * See [testing documentation](http://d.android.com/tools/testing).
13 | */
14 | @RunWith(AndroidJUnit4::class)
15 | class ExampleInstrumentedTest {
16 | @Test
17 | fun useAppContext() {
18 | // Context of the app under test.
19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
20 | assertEquals("com.instamobile.ui.fragment.onBoarding.walkthroughactivity", appContext.packageName)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
16 |
17 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
30 |
31 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/app/src/main/java/com/instamobile/firebaseStarterKit/adapter/BindingAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.instamobile.firebaseStarterKit.adapter
2 |
3 | import android.view.View
4 | import android.widget.ImageView
5 | import android.widget.TextView
6 | import androidx.databinding.BindingAdapter
7 | import com.instamobile.ui.fragment.onBoarding.walkthroughactivity.R
8 | import com.squareup.picasso.Picasso
9 |
10 | @BindingAdapter("hideIfFalse")
11 | fun hideIfFalse(view: View, boolean: Boolean) {
12 | if (boolean) view.visibility = View.VISIBLE else view.visibility = View.GONE
13 | }
14 |
15 | @BindingAdapter("hideIfEmpty")
16 | fun hideIfEmpty(textView: TextView, error: String?) {
17 | if (error != null) {
18 | if (error.isEmpty()) {
19 | textView.visibility = View.INVISIBLE
20 | } else {
21 | textView.visibility = View.VISIBLE
22 | textView.text = error
23 | }
24 | }
25 | }
26 |
27 | @BindingAdapter("setImage")
28 | fun setImage(imageView: ImageView, url: String?) {
29 | if (!url.isNullOrEmpty()) {
30 | Picasso.get().load(url).placeholder(R.drawable.placeholder).into(imageView)
31 | } else {
32 | imageView.setImageResource(R.drawable.placeholder)
33 | }
34 |
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/instamobile/firebaseStarterKit/adapter/SliderAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.instamobile.firebaseStarterKit.adapter
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import androidx.recyclerview.widget.RecyclerView
6 | import com.instamobile.firebaseStarterKit.model.SlideContent
7 | import com.instamobile.ui.fragment.onBoarding.walkthroughactivity.databinding.SlideBinding
8 |
9 | class SliderAdapter : RecyclerView.Adapter() {
10 | var list: List = listOf()
11 |
12 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): OnBoardingViewHolder {
13 | val view = SlideBinding.inflate(LayoutInflater.from(parent.context), parent, false)
14 | return OnBoardingViewHolder(view)
15 | }
16 |
17 | override fun getItemCount(): Int {
18 | return list.size
19 | }
20 |
21 | override fun onBindViewHolder(holder: OnBoardingViewHolder, position: Int) {
22 | holder.bind(list[position])
23 | }
24 |
25 | fun setItems(newList: List) {
26 | list = newList
27 | notifyDataSetChanged()
28 | }
29 | }
30 |
31 | class OnBoardingViewHolder(private val binding: SlideBinding) :
32 | RecyclerView.ViewHolder(binding.root) {
33 |
34 | fun bind(slideContent: SlideContent) {
35 | binding.content = slideContent
36 | }
37 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/instamobile/firebaseStarterKit/model/SlideContent.kt:
--------------------------------------------------------------------------------
1 | package com.instamobile.firebaseStarterKit.model
2 |
3 | import android.graphics.drawable.Drawable
4 |
5 | data class SlideContent(val image: Drawable, val header: String, val description: String)
--------------------------------------------------------------------------------
/app/src/main/java/com/instamobile/firebaseStarterKit/model/UserModel.kt:
--------------------------------------------------------------------------------
1 | package com.instamobile.firebaseStarterKit.model
2 |
3 | data class UserModel(
4 | var userID: String = "",
5 | var profilePictureURL: String = "",
6 | var phoneNumber: String = "",
7 | var active: Boolean = false,
8 | var firstName: String = "",
9 | var lastName: String = "",
10 | var email: String = "",
11 | var fcmToken: String = "",
12 | var selected: Boolean = false,
13 | var userName: String = ""
14 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/instamobile/firebaseStarterKit/ui/activity/host/HostActivity.kt:
--------------------------------------------------------------------------------
1 | package com.instamobile.firebaseStarterKit.ui.activity.host
2 |
3 | import android.content.Intent
4 | import android.os.Bundle
5 | import android.view.View
6 | import androidx.appcompat.app.AppCompatActivity
7 | import androidx.drawerlayout.widget.DrawerLayout
8 | import androidx.navigation.NavController
9 | import androidx.navigation.fragment.NavHostFragment
10 | import androidx.navigation.ui.NavigationUI
11 | import androidx.navigation.ui.setupWithNavController
12 | import com.google.android.gms.auth.api.signin.GoogleSignIn
13 | import com.google.android.gms.auth.api.signin.GoogleSignInClient
14 | import com.google.android.gms.auth.api.signin.GoogleSignInOptions
15 | import com.google.firebase.auth.FirebaseAuth
16 | import com.google.firebase.firestore.FirebaseFirestore
17 | import com.instamobile.firebaseStarterKit.model.UserModel
18 | import com.instamobile.firebaseStarterKit.utils.FirestoreUtil
19 | import com.instamobile.firebaseStarterKit.utils.MyApplication
20 | import com.instamobile.firebaseStarterKit.utils.Prefs
21 | import com.instamobile.ui.fragment.onBoarding.walkthroughactivity.R
22 | import com.instamobile.ui.fragment.onBoarding.walkthroughactivity.databinding.DrawerHeaderLayoutBinding
23 | import kotlinx.android.synthetic.main.activity_host.*
24 |
25 | class HostActivity : AppCompatActivity() {
26 | lateinit var googleSignInClient: GoogleSignInClient
27 | private lateinit var navController: NavController
28 | private val mAuth: FirebaseAuth = FirebaseAuth.getInstance()
29 | private val db: FirebaseFirestore = FirebaseFirestore.getInstance()
30 | private lateinit var drawerLayout: DrawerLayout
31 | private lateinit var navViewBinding: DrawerHeaderLayoutBinding
32 | override fun onCreate(savedInstanceState: Bundle?) {
33 | setTheme(R.style.AppTheme)
34 | super.onCreate(savedInstanceState)
35 | setContentView(R.layout.activity_host)
36 | val toolbar = customToolbar
37 | setSupportActionBar(toolbar)
38 |
39 | val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
40 | .requestIdToken(getString(R.string.default_web_client_id))
41 | .requestEmail()
42 | .build()
43 | googleSignInClient = GoogleSignIn.getClient(this, gso)
44 |
45 | drawerLayout = drawer_layout
46 | navViewBinding = DrawerHeaderLayoutBinding.inflate(layoutInflater, navView, true)
47 | val navHost =
48 | supportFragmentManager.findFragmentById(R.id.navHostFragment) as NavHostFragment
49 | navController = navHost.navController
50 |
51 | val navInflater = navController.navInflater
52 |
53 | val graph = navInflater.inflate(R.navigation.main_graph)
54 |
55 | navController.addOnDestinationChangedListener { _, destination, _ ->
56 | if (destination.id == R.id.onBoarding ||
57 | destination.id == R.id.authFragment ||
58 | destination.id == R.id.loginFragment ||
59 | destination.id == R.id.signUpFragment
60 | ) {
61 | toolbar.visibility = View.GONE
62 | drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
63 | } else {
64 | toolbar.visibility = View.VISIBLE
65 | drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
66 | }
67 | }
68 | if (!Prefs.getInstance(this)!!.hasCompletedWalkthrough!!) {
69 | if (mAuth.currentUser == null) {
70 | graph.startDestination = R.id.authFragment
71 | } else {
72 | getUserData()
73 | graph.startDestination = R.id.homeFragment
74 | }
75 | } else {
76 | graph.startDestination = R.id.onBoarding
77 |
78 | }
79 | navController.graph = graph
80 |
81 | NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout)
82 | navView.setupWithNavController(navController)
83 | navView.setNavigationItemSelectedListener {
84 | it.isChecked
85 | drawerLayout.closeDrawers()
86 | when (it.itemId) {
87 | R.id.action_logout -> {
88 | MyApplication.currentUser!!.active = false
89 | FirestoreUtil.updateUser(MyApplication.currentUser!!) {
90 | mAuth.signOut()
91 | }
92 | googleSignInClient.signOut()
93 | MyApplication.currentUser = null
94 | navController.navigate(R.id.action_logout)
95 | }
96 | }
97 | true
98 | }
99 | }
100 |
101 | private fun getUserData() {
102 |
103 | val ref = db.collection("users").document(mAuth.currentUser!!.uid)
104 |
105 | ref.get().addOnSuccessListener {
106 | val userInfo = it.toObject(UserModel::class.java)
107 | navViewBinding.user = userInfo
108 | MyApplication.currentUser = userInfo
109 | MyApplication.currentUser!!.active = true
110 | FirestoreUtil.updateUser(MyApplication.currentUser!!) {
111 | }
112 | }.addOnFailureListener {
113 | val intent = Intent(this, MyApplication::class.java)
114 | startActivity(intent)
115 | finish()
116 | }
117 | }
118 |
119 | override fun onSupportNavigateUp(): Boolean {
120 | return NavigationUI.navigateUp(navController, drawerLayout)
121 | }
122 |
123 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/instamobile/firebaseStarterKit/ui/fragment/authHost/AuthFragment.kt:
--------------------------------------------------------------------------------
1 | package com.instamobile.firebaseStarterKit.ui.fragment.authHost
2 |
3 |
4 | import android.os.Bundle
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import androidx.fragment.app.Fragment
9 | import androidx.lifecycle.Observer
10 | import androidx.lifecycle.ViewModelProviders
11 | import androidx.navigation.fragment.findNavController
12 | import com.instamobile.ui.fragment.onBoarding.walkthroughactivity.databinding.FragmentAuthBinding
13 |
14 | class AuthFragment : Fragment() {
15 | private lateinit var binding: FragmentAuthBinding
16 | private lateinit var viewModel: AuthViewModel
17 | override fun onCreateView(
18 | inflater: LayoutInflater, container: ViewGroup?,
19 | savedInstanceState: Bundle?
20 | ): View? {
21 | viewModel = ViewModelProviders.of(this).get(AuthViewModel::class.java)
22 | binding = FragmentAuthBinding.inflate(inflater, container, false)
23 | binding.authViewModel = viewModel
24 | binding.lifecycleOwner = this
25 |
26 | viewModel.navigateToLogin.observe(this, Observer {
27 | if (it) {
28 | this.findNavController()
29 | .navigate(AuthFragmentDirections.actionAuthFragmentToLoginFragment())
30 | viewModel.doneNavigationToLogin()
31 | }
32 | })
33 | viewModel.navigateToSignUp.observe(this, Observer {
34 | if (it) {
35 | this.findNavController()
36 | .navigate(AuthFragmentDirections.actionAuthFragmentToSignUpFragment())
37 | viewModel.doneNavigationToSignUp()
38 | }
39 | })
40 |
41 | return binding.root
42 | }
43 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/instamobile/firebaseStarterKit/ui/fragment/authHost/AuthViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.instamobile.firebaseStarterKit.ui.fragment.authHost
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import androidx.lifecycle.ViewModel
6 |
7 | class AuthViewModel : ViewModel() {
8 | private val _navigateToLogin = MutableLiveData().apply { value = false }
9 | val navigateToLogin: LiveData
10 | get() = _navigateToLogin
11 |
12 | private val _navigateToSignUp = MutableLiveData().apply { value = false }
13 | val navigateToSignUp: LiveData
14 | get() = _navigateToSignUp
15 |
16 | fun startNavigationToLogin() {
17 | _navigateToLogin.value = true
18 | }
19 |
20 | fun doneNavigationToLogin() {
21 | _navigateToLogin.value = false
22 | }
23 |
24 | fun navigateToSignUp() {
25 | _navigateToSignUp.value = true
26 | }
27 |
28 | fun doneNavigationToSignUp() {
29 | _navigateToSignUp.value = false
30 | }
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/instamobile/firebaseStarterKit/ui/fragment/home/HomeFragment.kt:
--------------------------------------------------------------------------------
1 | package com.instamobile.firebaseStarterKit.ui.fragment.home
2 |
3 |
4 | import android.os.Bundle
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import androidx.fragment.app.Fragment
9 | import com.instamobile.ui.fragment.onBoarding.walkthroughactivity.databinding.FragmentHomeBinding
10 |
11 | class HomeFragment : Fragment() {
12 | private lateinit var binding: FragmentHomeBinding
13 | override fun onCreateView(
14 | inflater: LayoutInflater, container: ViewGroup?,
15 | savedInstanceState: Bundle?
16 | ): View? {
17 | binding = FragmentHomeBinding.inflate(inflater, container, false)
18 |
19 | return binding.root
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/instamobile/firebaseStarterKit/ui/fragment/login/LoginFragment.kt:
--------------------------------------------------------------------------------
1 | package com.instamobile.firebaseStarterKit.ui.fragment.login
2 |
3 | import android.content.Intent
4 | import android.os.Bundle
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import androidx.core.widget.doAfterTextChanged
9 | import androidx.fragment.app.Fragment
10 | import androidx.lifecycle.Observer
11 | import androidx.lifecycle.ViewModelProviders
12 | import com.facebook.CallbackManager
13 | import com.facebook.FacebookCallback
14 | import com.facebook.FacebookException
15 | import com.facebook.login.LoginResult
16 | import com.instamobile.firebaseStarterKit.ui.activity.host.HostActivity
17 | import com.instamobile.ui.fragment.onBoarding.walkthroughactivity.databinding.FragmentLoginBinding
18 |
19 | class LoginFragment : Fragment() {
20 | private val RC_SIGN_IN = 1
21 | private lateinit var mCallbackManager: CallbackManager
22 | private lateinit var binding: FragmentLoginBinding
23 | private lateinit var viewModel: LoginViewModel
24 | override fun onCreateView(
25 | inflater: LayoutInflater, container: ViewGroup?,
26 | savedInstanceState: Bundle?
27 | ): View? {
28 | mCallbackManager = CallbackManager.Factory.create()
29 | viewModel = ViewModelProviders.of(this).get(LoginViewModel::class.java)
30 | binding = FragmentLoginBinding.inflate(inflater, container, false)
31 | binding.loginViewModel = viewModel
32 | binding.lifecycleOwner = this
33 |
34 | setupListeners()
35 | setUpObservers()
36 |
37 | binding.btLoginFacebook.setPermissions("email")
38 | binding.btLoginFacebook.registerCallback(mCallbackManager,
39 | object : FacebookCallback {
40 | override fun onSuccess(result: LoginResult) {
41 | viewModel.handleFacebookToken(result)
42 | }
43 |
44 | override fun onCancel() {
45 | viewModel.onFinishLoading()
46 | }
47 |
48 | override fun onError(error: FacebookException?) {
49 | viewModel.onFinishLoading()
50 | viewModel.setError(error?.message!!)
51 | }
52 | })
53 |
54 | return binding.root
55 | }
56 |
57 | private fun setupListeners() {
58 | binding.etEmail.doAfterTextChanged { email -> viewModel.username = email.toString() }
59 | binding.etPassword.doAfterTextChanged { pass -> viewModel.password = pass.toString() }
60 | binding.signInButton.setOnClickListener {
61 | signInWithGoogle()
62 | }
63 | }
64 |
65 | private fun setUpObservers() {
66 | viewModel.navigateToHome.observe(this, Observer {
67 | if (it) {
68 | startActivity(Intent(context, HostActivity::class.java))
69 | (activity as HostActivity).finish()
70 | viewModel.doneHomeNavigation()
71 | }
72 | })
73 | }
74 |
75 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
76 | super.onActivityResult(requestCode, resultCode, data)
77 | mCallbackManager.onActivityResult(requestCode, resultCode, data)
78 | if (requestCode == RC_SIGN_IN) {
79 | viewModel.handleGoogleSignInResult(data)
80 | }
81 |
82 | }
83 |
84 | private fun signInWithGoogle() {
85 | val signInIntent = (activity as HostActivity).googleSignInClient.signInIntent
86 | startActivityForResult(signInIntent, RC_SIGN_IN)
87 | }
88 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/instamobile/firebaseStarterKit/ui/fragment/login/LoginViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.instamobile.firebaseStarterKit.ui.fragment.login
2 |
3 | import android.content.Intent
4 | import android.os.Bundle
5 | import androidx.lifecycle.LiveData
6 | import androidx.lifecycle.MutableLiveData
7 | import androidx.lifecycle.ViewModel
8 | import com.facebook.GraphRequest
9 | import com.facebook.login.LoginResult
10 | import com.google.android.gms.auth.api.signin.GoogleSignIn
11 | import com.google.android.gms.auth.api.signin.GoogleSignInAccount
12 | import com.google.android.gms.common.api.ApiException
13 | import com.google.firebase.auth.FacebookAuthProvider
14 | import com.google.firebase.auth.FirebaseAuth
15 | import com.google.firebase.auth.FirebaseUser
16 | import com.google.firebase.auth.GoogleAuthProvider
17 | import com.google.firebase.firestore.FirebaseFirestore
18 | import com.instamobile.firebaseStarterKit.model.UserModel
19 | import com.instamobile.firebaseStarterKit.utils.FirestoreUtil
20 | import com.instamobile.firebaseStarterKit.utils.MyApplication
21 | import org.json.JSONObject
22 |
23 | class LoginViewModel : ViewModel() {
24 | private var mAuth: FirebaseAuth = FirebaseAuth.getInstance()
25 | private var db: FirebaseFirestore = FirebaseFirestore.getInstance()
26 |
27 | var username: String = ""
28 | set(value) {
29 | field = value
30 | validateInput()
31 | }
32 |
33 | var password: String = ""
34 | set(value) {
35 | field = value
36 | validateInput()
37 | }
38 |
39 | private val _buttonEnabled = MutableLiveData()
40 | val buttonEnabled: LiveData
41 | get() = _buttonEnabled
42 |
43 | private val _progress = MutableLiveData()
44 | val progress: LiveData
45 | get() = _progress
46 |
47 | private val _errorString = MutableLiveData()
48 | val errorString: LiveData
49 | get() = _errorString
50 |
51 | private fun validateInput() {
52 | _buttonEnabled.value = !(username.isEmpty() || password.isEmpty())
53 | }
54 |
55 | private val _navigateToHome = MutableLiveData()
56 | val navigateToHome: LiveData
57 | get() = _navigateToHome
58 |
59 | fun login() {
60 | onStartLoading()
61 | mAuth.signInWithEmailAndPassword(username, password).addOnCompleteListener {
62 | if (it.isSuccessful) {
63 | getFirebaseUserData()
64 | } else {
65 | onFinishLoading()
66 | _errorString.value = it.exception?.message
67 | }
68 | }
69 |
70 | }
71 |
72 | private fun getFirebaseUserData() {
73 | val ref = db.collection("users").document(mAuth.currentUser!!.uid)
74 | ref.get().addOnSuccessListener {
75 | val userInfo = it.toObject(UserModel::class.java)
76 | MyApplication.currentUser = userInfo
77 | MyApplication.currentUser!!.active = true
78 | FirestoreUtil.updateUser(MyApplication.currentUser!!) {
79 |
80 | }
81 | onFinishLoading()
82 | startHomeNavigation()
83 | }.addOnFailureListener {
84 | onFinishLoading()
85 | _errorString.value = it.message
86 | }
87 |
88 | }
89 |
90 | fun handleFacebookToken(result: LoginResult) {
91 | onStartLoading()
92 | val credential =
93 | FacebookAuthProvider.getCredential(result.accessToken.token.toString())
94 | mAuth.signInWithCredential(credential)
95 | .addOnCompleteListener { task ->
96 | if (task.isSuccessful) {
97 | val userId = task.result!!.user!!.uid
98 | db.collection("users").document(userId)
99 | .get()
100 | .addOnCompleteListener {
101 | if (it.isSuccessful) {
102 | val document = it.result
103 |
104 | if (document!!["email"] != null) {
105 | onFinishLoading()
106 | val userInfo = document.toObject(UserModel::class.java)
107 | MyApplication.currentUser = userInfo
108 | startHomeNavigation()
109 | } else {
110 | createUser(result)
111 | }
112 | } else {
113 | onFinishLoading()
114 | _errorString.value = it.exception?.message
115 | }
116 | }
117 |
118 | } else {
119 | onFinishLoading()
120 | _errorString.value = "Authentication failed."
121 | }
122 | }
123 | }
124 |
125 | private fun createUser(loginResult: LoginResult) {
126 |
127 | val userId = mAuth.currentUser!!.uid
128 |
129 | val request = GraphRequest.newMeRequest(loginResult.accessToken) { `object`, response ->
130 | try {
131 |
132 | val items = HashMap()
133 | items["email"] = `object`.get("email").toString()
134 | items["firstName"] = `object`.get("name").toString()
135 | items["lastName"] = ""
136 | items["userName"] = ""
137 | items["phoneNumber"] = "000000"
138 | items["userID"] = userId
139 | items["active"] = true
140 | // items["fcmToken"] = FirebaseInstanceId.getInstance().getToken().toString()
141 |
142 | val picture: JSONObject = `object`.get("picture") as JSONObject
143 | val data: JSONObject = picture.getJSONObject("data")
144 | val url = data.getString("url")
145 | items["profilePictureURL"] = url
146 | saveSocialUserToFirebase(mAuth.currentUser, items)
147 | } catch (e: Exception) {
148 | onFinishLoading()
149 | _errorString.value = e.message
150 | e.printStackTrace()
151 | }
152 | }
153 |
154 | val parameters = Bundle()
155 | parameters.putString("fields", "name,email,id,picture.type(large)")
156 | request.parameters = parameters
157 | request.executeAsync()
158 | }
159 |
160 | private fun saveSocialUserToFirebase(
161 | currentUser: FirebaseUser?,
162 | items: java.util.HashMap
163 | ) {
164 | db.collection("users").document(currentUser!!.uid).set(items).addOnSuccessListener {
165 | val userInfo = UserModel()
166 | userInfo.userID = currentUser.uid
167 | userInfo.email = items["email"].toString()
168 | userInfo.firstName = items["firstName"].toString()
169 | userInfo.lastName = items["lastName"].toString()
170 | userInfo.userName = items["userName"].toString()
171 | userInfo.profilePictureURL = items["profilePictureURL"].toString()
172 | userInfo.active = true
173 | // userInfo.fcmToken = items["fcmToken"].toString()
174 | MyApplication.currentUser = userInfo
175 |
176 | onFinishLoading()
177 | startHomeNavigation()
178 | }.addOnFailureListener {
179 | _errorString.value = it.message
180 | onFinishLoading()
181 | }
182 |
183 | }
184 |
185 | private fun startHomeNavigation() {
186 | _navigateToHome.value = true
187 |
188 | }
189 |
190 | fun doneHomeNavigation() {
191 | _navigateToHome.value = false
192 | }
193 |
194 | private fun onStartLoading() {
195 | _buttonEnabled.value = false
196 | _progress.value = true
197 | }
198 |
199 | fun onFinishLoading() {
200 | _buttonEnabled.value = true
201 | _progress.value = false
202 | }
203 |
204 | fun setError(error: String) {
205 | _errorString.value = error
206 | }
207 |
208 | fun handleGoogleSignInResult(data: Intent?) {
209 | val task = GoogleSignIn.getSignedInAccountFromIntent(data)
210 | try {
211 | val account = task.getResult(ApiException::class.java)
212 | firebaseAuthWithGoogle(account!!)
213 | } catch (e: ApiException) {
214 | e.printStackTrace()
215 | _errorString.value = e.message
216 | }
217 | }
218 |
219 | private fun firebaseAuthWithGoogle(account: GoogleSignInAccount) {
220 | onStartLoading()
221 | val credential = GoogleAuthProvider.getCredential(account.idToken, null)
222 | mAuth.signInWithCredential(credential).addOnCompleteListener {
223 | if (it.isSuccessful) {
224 | val userId = it.result!!.user!!.uid
225 | db.collection("users").document(userId)
226 | .get()
227 | .addOnCompleteListener { task ->
228 | if (task.isSuccessful) {
229 | val document = task.result
230 |
231 | if (document!!["email"] != null) {
232 | onFinishLoading()
233 | val userInfo = document.toObject(UserModel::class.java)
234 | MyApplication.currentUser = userInfo
235 | FirestoreUtil.updateUser(MyApplication.currentUser!!) {
236 | }
237 | startHomeNavigation()
238 | } else {
239 | createGoogleUser(account)
240 | }
241 | } else {
242 | onFinishLoading()
243 | _errorString.value = task.exception?.message
244 | }
245 | }
246 |
247 | } else {
248 | // If sign in fails, display a message to the user.
249 | onFinishLoading()
250 | _errorString.value = "Authentication failed."
251 | }
252 | }
253 |
254 | }
255 |
256 | private fun createGoogleUser(account: GoogleSignInAccount) {
257 | val userId = mAuth.currentUser!!.uid
258 | try {
259 | val items = HashMap()
260 | items["email"] = account.email!!
261 | items["firstName"] = account.displayName!!
262 | items["lastName"] = ""
263 | items["userName"] = ""
264 | items["phoneNumber"] = "000000"
265 | items["userID"] = userId
266 | items["active"] = true
267 | items["profilePictureURL"] = account.photoUrl.toString()
268 | saveSocialUserToFirebase(mAuth.currentUser, items)
269 | } catch (e: Exception) {
270 | onFinishLoading()
271 | _errorString.value = e.message
272 | e.printStackTrace()
273 | }
274 | }
275 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/instamobile/firebaseStarterKit/ui/fragment/onBoarding/OnBoarding.kt:
--------------------------------------------------------------------------------
1 | package com.instamobile.firebaseStarterKit.ui.fragment.onBoarding
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.fragment.app.Fragment
8 | import androidx.lifecycle.Observer
9 | import androidx.lifecycle.ViewModelProviders
10 | import androidx.navigation.fragment.findNavController
11 | import com.google.android.material.tabs.TabLayout
12 | import com.google.android.material.tabs.TabLayoutMediator
13 | import com.instamobile.firebaseStarterKit.adapter.SliderAdapter
14 | import com.instamobile.firebaseStarterKit.utils.Prefs
15 | import com.instamobile.ui.fragment.onBoarding.walkthroughactivity.databinding.FragmentOnBoardingBinding
16 | import kotlinx.android.synthetic.main.fragment_on_boarding.*
17 |
18 | class OnBoarding : Fragment() {
19 | private var sliderAdapter: SliderAdapter =
20 | SliderAdapter()
21 | private lateinit var viewModel: OnBoardingViewModel
22 | private lateinit var binding: FragmentOnBoardingBinding
23 |
24 | override fun onCreateView(
25 | inflater: LayoutInflater, container: ViewGroup?,
26 | savedInstanceState: Bundle?
27 | ): View? {
28 | viewModel = ViewModelProviders.of(this).get(OnBoardingViewModel::class.java)
29 | binding = FragmentOnBoardingBinding.inflate(inflater, container, false)
30 | binding.onBoardingViewModel = viewModel
31 | binding.lifecycleOwner = this
32 |
33 | viewModel.dataSet.observe(this, Observer {
34 | viewPager2.adapter = sliderAdapter
35 | sliderAdapter.setItems(it)
36 | TabLayoutMediator(
37 | indicator,
38 | viewPager2,
39 | object : TabLayoutMediator.TabConfigurationStrategy {
40 | override fun onConfigureTab(tab: TabLayout.Tab, position: Int) {
41 | viewPager2.setCurrentItem(tab.position, true)
42 | }
43 | }).attach()
44 | })
45 | viewModel.startNavigation.observe(this, Observer {
46 | if (it) {
47 | this.findNavController()
48 | .navigate(OnBoardingDirections.actionOnBoardingToAuthFragment())
49 | Prefs.getInstance(context!!)!!.hasCompletedWalkthrough = false
50 | viewModel.doneNavigation()
51 | }
52 | })
53 | binding.viewPager2.registerOnPageChangeCallback(viewModel.pagerCallBack)
54 | return binding.root
55 | }
56 |
57 | override fun onDestroyView() {
58 | super.onDestroyView()
59 | viewPager2.unregisterOnPageChangeCallback(viewModel.pagerCallBack)
60 | }
61 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/instamobile/firebaseStarterKit/ui/fragment/onBoarding/OnBoardingViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.instamobile.firebaseStarterKit.ui.fragment.onBoarding
2 |
3 | import android.app.Application
4 | import androidx.core.content.ContextCompat
5 | import androidx.lifecycle.AndroidViewModel
6 | import androidx.lifecycle.LiveData
7 | import androidx.lifecycle.MutableLiveData
8 | import androidx.viewpager2.widget.ViewPager2
9 | import com.instamobile.firebaseStarterKit.model.SlideContent
10 | import com.instamobile.ui.fragment.onBoarding.walkthroughactivity.R
11 |
12 | class OnBoardingViewModel(application: Application) : AndroidViewModel(application) {
13 | private val list = listOf(
14 | SlideContent(
15 | ContextCompat.getDrawable(application.applicationContext, R.drawable.ic_fast)!!,
16 | "Move Fast",
17 | "Use our starter kit in Kotlin to build your apps faster"
18 | ),
19 | SlideContent(
20 | ContextCompat.getDrawable(application.applicationContext, R.drawable.ic_kotlin)!!,
21 | "Learn Kotlin",
22 | "Learning Kotlin practically by working on a real project"
23 | ),
24 | SlideContent(
25 | ContextCompat.getDrawable(application.applicationContext, R.drawable.ic_firebase)!!,
26 | "Learn Firebase",
27 | "Learn how to use Firebase as a backend for your Kotlin app"
28 | ),
29 | SlideContent(
30 | ContextCompat.getDrawable(application.applicationContext, R.drawable.ic_save_time)!!,
31 | "Save Time",
32 | "Save a few days of development by starting with our app template"
33 | )
34 | )
35 |
36 | private val _dataSet = MutableLiveData>().apply { value = list }
37 | val dataSet: LiveData>
38 | get() = _dataSet
39 |
40 | private val _buttonVisiability = MutableLiveData().apply { value = false }
41 | val buttonVisiability: LiveData
42 | get() = _buttonVisiability
43 |
44 | val pagerCallBack = object : ViewPager2.OnPageChangeCallback() {
45 | override fun onPageSelected(position: Int) {
46 | _buttonVisiability.value = position == list.size - 1
47 | super.onPageSelected(position)
48 | }
49 | }
50 | private val _startNavigation = MutableLiveData().apply { value = false }
51 | val startNavigation: LiveData
52 | get() = _startNavigation
53 |
54 | fun navigateToAuth() {
55 | _startNavigation.value = true
56 | }
57 |
58 | fun doneNavigation() {
59 | _startNavigation.value = false
60 | }
61 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/instamobile/firebaseStarterKit/ui/fragment/signUp/SignUpFragment.kt:
--------------------------------------------------------------------------------
1 | package com.instamobile.firebaseStarterKit.ui.fragment.signUp
2 |
3 | import android.Manifest
4 | import android.app.Activity
5 | import android.content.Intent
6 | import android.content.pm.PackageManager
7 | import android.net.Uri
8 | import android.os.Build
9 | import android.os.Bundle
10 | import android.provider.MediaStore
11 | import android.view.LayoutInflater
12 | import android.view.View
13 | import android.view.ViewGroup
14 | import androidx.core.content.ContextCompat
15 | import androidx.core.widget.doAfterTextChanged
16 | import androidx.fragment.app.Fragment
17 | import androidx.lifecycle.Observer
18 | import androidx.lifecycle.ViewModelProviders
19 | import com.instamobile.firebaseStarterKit.ui.activity.host.HostActivity
20 | import com.instamobile.ui.fragment.onBoarding.walkthroughactivity.R
21 | import com.instamobile.ui.fragment.onBoarding.walkthroughactivity.databinding.FragmentSignUpBinding
22 | import com.squareup.picasso.Picasso
23 |
24 |
25 | class SignUpFragment : Fragment() {
26 |
27 |
28 | private val PERMISSION_CODE = 1
29 | private val PICK_IMAGE_CODE = 2
30 | private var imageUri: Uri? = null
31 | private lateinit var binding: FragmentSignUpBinding
32 | private lateinit var viewModel: SignUpViewModel
33 | override fun onCreateView(
34 | inflater: LayoutInflater, container: ViewGroup?,
35 | savedInstanceState: Bundle?
36 | ): View? {
37 | viewModel = ViewModelProviders.of(this).get(SignUpViewModel::class.java)
38 | binding = FragmentSignUpBinding.inflate(inflater, container, false)
39 | binding.signUpViewModel = viewModel
40 | binding.lifecycleOwner = this
41 | setupListeners()
42 | setupObservers()
43 |
44 | return binding.root
45 | }
46 |
47 | private fun setupListeners() {
48 | binding.etFullName.doAfterTextChanged { fullname ->
49 | viewModel.fullname = fullname.toString()
50 | }
51 | binding.etPhone.doAfterTextChanged { phone -> viewModel.phoneNumber = phone.toString() }
52 | binding.etEmail.doAfterTextChanged { email -> viewModel.email = email.toString() }
53 | binding.etPassword.doAfterTextChanged { password ->
54 | viewModel.password = password.toString()
55 | }
56 | binding.selectPhoto.setOnClickListener {
57 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
58 | checkPermissions()
59 | } else {
60 | pickFromGallery()
61 | }
62 | }
63 | }
64 |
65 | private fun setupObservers() {
66 | viewModel.fullNameError.observe(this, Observer {
67 | if (it.isNotEmpty()) {
68 | binding.etFullName.error = it
69 | }
70 | })
71 | viewModel.phoneNumberError.observe(this, Observer {
72 | if (it.isNotEmpty()) {
73 | binding.etPhone.error = it
74 | }
75 | })
76 | viewModel.emailError.observe(this, Observer {
77 | if (it.isNotEmpty()) {
78 | binding.etEmail.error = it
79 | }
80 | })
81 | viewModel.passwordError.observe(this, Observer {
82 | if (it.isNotEmpty()) {
83 | binding.etPassword.error = it
84 | }
85 | })
86 | viewModel.navigateToHome.observe(this, Observer {
87 | if (it) {
88 | startActivity(Intent(context, HostActivity::class.java))
89 | (activity as HostActivity).finish()
90 | viewModel.doneNavigating()
91 | }
92 | })
93 | }
94 |
95 | override fun onRequestPermissionsResult(
96 | requestCode: Int,
97 | permissions: Array,
98 | grantResults: IntArray
99 | ) {
100 | super.onRequestPermissionsResult(requestCode, permissions, grantResults)
101 | if (requestCode == PERMISSION_CODE) {
102 | if (permissions[0] == Manifest.permission.READ_EXTERNAL_STORAGE) {
103 | if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
104 | pickFromGallery()
105 | }
106 | }
107 | }
108 | }
109 |
110 | private fun checkPermissions() {
111 | if (ContextCompat.checkSelfPermission(
112 | context!!,
113 | Manifest.permission.READ_EXTERNAL_STORAGE
114 | ) != PackageManager.PERMISSION_GRANTED
115 | ) {
116 | requestPermissions(
117 | arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), PERMISSION_CODE
118 | )
119 | } else {
120 | pickFromGallery()
121 | }
122 | }
123 |
124 | private fun pickFromGallery() {
125 | val getIntent = Intent(Intent.ACTION_GET_CONTENT)
126 | getIntent.type = "image/*"
127 |
128 | val pickIntent = Intent(
129 | Intent.ACTION_PICK,
130 | MediaStore.Images.Media.EXTERNAL_CONTENT_URI
131 | )
132 | pickIntent.type = "image/*"
133 |
134 | val chooserIntent = Intent.createChooser(getIntent, getString(R.string.select_image))
135 | chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, arrayOf(pickIntent))
136 | startActivityForResult(chooserIntent, PICK_IMAGE_CODE)
137 | }
138 |
139 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
140 | super.onActivityResult(requestCode, resultCode, data)
141 | if (requestCode == PICK_IMAGE_CODE && resultCode == Activity.RESULT_OK) {
142 | if (data != null) {
143 | imageUri = data.data
144 | viewModel.imageUri = imageUri
145 | Picasso.get().load(imageUri).placeholder(R.drawable.placeholder)
146 | .into(binding.ciAvatar)
147 | }
148 | }
149 | }
150 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/instamobile/firebaseStarterKit/ui/fragment/signUp/SignUpViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.instamobile.firebaseStarterKit.ui.fragment.signUp
2 |
3 | import android.app.Application
4 | import android.net.Uri
5 | import android.util.Log
6 | import android.util.Patterns
7 | import androidx.lifecycle.AndroidViewModel
8 | import androidx.lifecycle.LiveData
9 | import androidx.lifecycle.MutableLiveData
10 | import com.google.firebase.auth.FirebaseAuth
11 | import com.google.firebase.auth.FirebaseUser
12 | import com.google.firebase.firestore.FirebaseFirestore
13 | import com.google.firebase.storage.FirebaseStorage
14 | import com.instamobile.firebaseStarterKit.model.UserModel
15 | import com.instamobile.firebaseStarterKit.utils.MyApplication
16 | import com.instamobile.ui.fragment.onBoarding.walkthroughactivity.R
17 |
18 | class SignUpViewModel(private val myApplication: Application) : AndroidViewModel(myApplication) {
19 | private val auth = FirebaseAuth.getInstance()
20 | private val database = FirebaseFirestore.getInstance()
21 |
22 | var fullname: String = ""
23 | var phoneNumber: String = ""
24 | var email: String = ""
25 | var password: String = ""
26 | var imageUri: Uri? = null
27 |
28 | private val _fullNameError = MutableLiveData()
29 | val fullNameError: LiveData
30 | get() = _fullNameError
31 |
32 | private val _phoneNumberError = MutableLiveData()
33 | val phoneNumberError: LiveData
34 | get() = _phoneNumberError
35 |
36 | private val _emailError = MutableLiveData()
37 | val emailError: LiveData
38 | get() = _emailError
39 |
40 | private val _passwordError = MutableLiveData()
41 | val passwordError: LiveData
42 | get() = _passwordError
43 |
44 | private val _buttonEnabled = MutableLiveData().apply { value = true }
45 | val buttonEnabled: LiveData
46 | get() = _buttonEnabled
47 |
48 | private val _navigateToHome = MutableLiveData().apply { value = false }
49 | val navigateToHome: LiveData
50 | get() = _navigateToHome
51 |
52 | private val _signUpError = MutableLiveData()
53 | val signUpError: LiveData
54 | get() = _signUpError
55 |
56 | fun createUser() {
57 | when {
58 | fullname.isEmpty() -> _passwordError.value =
59 | myApplication.getString(R.string.name_required_error)
60 | phoneNumber.isEmpty() -> _phoneNumberError.value =
61 | myApplication.getString(R.string.phone_required_error)
62 | email.isEmpty() -> _emailError.value =
63 | myApplication.getString(R.string.email_required_error)
64 | password.isEmpty() -> _passwordError.value =
65 | myApplication.getString(R.string.password_required_error)
66 | !Patterns.PHONE.matcher(phoneNumber).matches() -> _phoneNumberError.value =
67 | myApplication.getString(R.string.malformed_phone_error)
68 | !Patterns.EMAIL_ADDRESS.matcher(email).matches() -> _emailError.value =
69 | myApplication.getString(R.string.malformed_email_error)
70 | password.length < 6 -> _passwordError.value =
71 | myApplication.getString(R.string.short_password_error)
72 | else -> {
73 | _buttonEnabled.value = false
74 | //create user without image
75 |
76 | auth.createUserWithEmailAndPassword(email, password).addOnSuccessListener {
77 |
78 | if (imageUri == null) {
79 | createUserWithoutImage()
80 | } else {
81 | createUserWithImage()
82 | }
83 | }.addOnFailureListener {
84 | it.printStackTrace()
85 | _signUpError.value = it.message
86 | _buttonEnabled.value = true
87 |
88 | }
89 | }
90 | }
91 | }
92 |
93 | private fun createUserWithoutImage() {
94 | val userid = auth.currentUser!!.uid
95 | val items = HashMap()
96 | items["email"] = email
97 | items["firstName"] = fullname
98 | items["lastName"] = ""
99 | items["userName"] = ""
100 | items["phoneNumber"] = phoneNumber
101 | items["userID"] = userid
102 | items["profilePictureURL"] = ""
103 | items["active"] = true
104 | saveUserToDatabase(auth.currentUser!!, items)
105 |
106 | }
107 |
108 | private fun createUserWithImage() {
109 | val data = FirebaseStorage.getInstance().reference
110 | val photoRef = data.child("images/" + auth.currentUser!!.uid + ".png")
111 | photoRef.putFile(this.imageUri!!).addOnProgressListener {
112 | }.continueWithTask { task ->
113 | if (!task.isSuccessful) {
114 | throw task.exception!!
115 | }
116 | photoRef.downloadUrl
117 | }.addOnSuccessListener { downloadUri ->
118 |
119 | val userId = auth.currentUser!!.uid
120 | val items = HashMap()
121 | items["email"] = email
122 | items["firstName"] = fullname
123 | items["lastName"] = ""
124 | items["userName"] = ""
125 | items["phoneNumber"] = phoneNumber
126 | items["userID"] = userId
127 | items["profilePictureURL"] = downloadUri.toString()
128 | items["active"] = true
129 | saveUserToDatabase(auth.currentUser!!, items)
130 | }
131 | }
132 |
133 | private fun saveUserToDatabase(user: FirebaseUser, items: HashMap) {
134 |
135 | database.collection("users").document(user.uid).set(items)
136 | .addOnSuccessListener {
137 | val userModel = UserModel()
138 | userModel.userID = user.uid
139 | userModel.email = items["email"].toString()
140 | userModel.firstName = items["firstName"].toString()
141 | userModel.lastName = items["lastName"].toString()
142 | userModel.userName = items["userName"].toString()
143 | userModel.profilePictureURL = items["profilePictureURL"].toString()
144 | userModel.active = true
145 | MyApplication.currentUser = userModel
146 | Log.d("SignUp state", "save user:success")
147 | _navigateToHome.value = true
148 | }.addOnFailureListener {
149 | _signUpError.value = it.message
150 | _buttonEnabled.value = true
151 | }
152 | }
153 |
154 | fun doneNavigating() {
155 | _navigateToHome.value = false
156 | }
157 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/instamobile/firebaseStarterKit/utils/AppLifecycleHandler.kt:
--------------------------------------------------------------------------------
1 | package com.instamobile.firebaseStarterKit.utils
2 |
3 | import android.app.Activity
4 | import android.app.Application
5 | import android.content.ComponentCallbacks2
6 | import android.content.res.Configuration
7 | import android.os.Bundle
8 |
9 | class AppLifecycleHandler(private val lifeCycleDelegate: LifeCycleDelegate) :
10 | Application.ActivityLifecycleCallbacks,
11 | ComponentCallbacks2 {
12 |
13 | private var appInForeground = false
14 |
15 | override fun onActivityPaused(p0: Activity?) {}
16 |
17 | override fun onActivityResumed(p0: Activity?) {
18 | if (!appInForeground) {
19 | appInForeground = true
20 | lifeCycleDelegate.onAppForegrounded()
21 | }
22 | }
23 |
24 | override fun onActivityStarted(p0: Activity?) {
25 | }
26 |
27 | override fun onActivityDestroyed(p0: Activity?) {
28 | }
29 |
30 | override fun onActivitySaveInstanceState(p0: Activity?, p1: Bundle?) {
31 | }
32 |
33 | override fun onActivityStopped(p0: Activity?) {
34 | }
35 |
36 | override fun onActivityCreated(p0: Activity?, p1: Bundle?) {
37 | }
38 |
39 | override fun onLowMemory() {}
40 |
41 | override fun onConfigurationChanged(p0: Configuration?) {}
42 |
43 | override fun onTrimMemory(level: Int) {
44 | if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
45 | appInForeground = false
46 | lifeCycleDelegate.onAppBackgrounded()
47 | }
48 | }
49 |
50 | }
51 |
52 | interface LifeCycleDelegate {
53 | fun onAppBackgrounded()
54 | fun onAppForegrounded()
55 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/instamobile/firebaseStarterKit/utils/FirestoreUtil.kt:
--------------------------------------------------------------------------------
1 | package com.instamobile.firebaseStarterKit.utils
2 |
3 | import com.google.firebase.auth.FirebaseAuth
4 | import com.google.firebase.firestore.DocumentReference
5 | import com.google.firebase.firestore.FirebaseFirestore
6 | import com.instamobile.firebaseStarterKit.model.UserModel
7 |
8 | object FirestoreUtil {
9 | val firestoreInstance: FirebaseFirestore by lazy { FirebaseFirestore.getInstance() }
10 |
11 | private val currentUserDocRef: DocumentReference
12 | get() = firestoreInstance.document(
13 | "users/${FirebaseAuth.getInstance().currentUser?.uid
14 | ?: throw NullPointerException("UID is null.")}"
15 | )
16 |
17 | fun updateUser(userModel: UserModel, onComplete: (String) -> Unit) {
18 |
19 | val task = currentUserDocRef.set(userModel)
20 |
21 | task.continueWith {
22 | if (it.isSuccessful) {
23 | onComplete("success")
24 | }
25 | }.addOnFailureListener {
26 | onComplete("failure")
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/instamobile/firebaseStarterKit/utils/MyApplication.kt:
--------------------------------------------------------------------------------
1 | package com.instamobile.firebaseStarterKit.utils
2 |
3 | import android.app.Application
4 | import android.content.Context
5 | import android.util.Log
6 | import androidx.lifecycle.Lifecycle
7 | import androidx.lifecycle.LifecycleObserver
8 | import androidx.lifecycle.OnLifecycleEvent
9 | import androidx.lifecycle.ProcessLifecycleOwner
10 | import androidx.multidex.MultiDex
11 | import androidx.multidex.MultiDexApplication
12 | import com.google.firebase.auth.FirebaseAuth
13 | import com.instamobile.firebaseStarterKit.model.UserModel
14 |
15 |
16 | class MyApplication : MultiDexApplication(), LifeCycleDelegate {
17 |
18 | companion object {
19 | var currentUser: UserModel? = null
20 | }
21 |
22 | override fun onCreate() {
23 | super.onCreate()
24 | val lifeCycleHandler = AppLifecycleHandler(this)
25 | registerLifecycleHandler(lifeCycleHandler)
26 | }
27 |
28 | override fun attachBaseContext(context: Context) {
29 | super.attachBaseContext(context)
30 | MultiDex.install(this)
31 | }
32 |
33 | override fun onAppBackgrounded() {
34 | val mAuth: FirebaseAuth? = FirebaseAuth.getInstance()
35 |
36 | if (mAuth!!.currentUser != null && currentUser != null) {
37 | currentUser!!.active = false
38 | FirestoreUtil.updateUser(currentUser!!) {
39 |
40 | }
41 | }
42 | Log.d("Awww", "App in background")
43 | }
44 |
45 | override fun onAppForegrounded() {
46 | val mAuth: FirebaseAuth? = FirebaseAuth.getInstance()
47 |
48 | if (mAuth!!.currentUser != null && currentUser != null) {
49 | currentUser!!.active = true
50 | FirestoreUtil.updateUser(currentUser!!) {
51 |
52 | }
53 | }
54 |
55 | Log.d("Yeeey", "App in foreground")
56 | }
57 |
58 | private fun registerLifecycleHandler(lifeCycleHandler: AppLifecycleHandler) {
59 | registerActivityLifecycleCallbacks(lifeCycleHandler)
60 | registerComponentCallbacks(lifeCycleHandler)
61 | }
62 | }
63 |
64 | /**
65 | * If you are using Android Architecture Components you can use the ProcessLifecycleOwner
66 | * and LifecycleObserver like so (set this class to the app name in the manifest)
67 | * // ? = null
32 | private var attached: Boolean = false
33 |
34 | private var onPageChangeCallback: TabLayoutOnPageChangeCallback? = null
35 | private var onTabSelectedListener: TabLayout.OnTabSelectedListener? = null
36 | private var pagerAdapterObserver: RecyclerView.AdapterDataObserver? = null
37 |
38 | /**
39 | * A callback interface that must be implemented to set the text and styling of newly created
40 | * tabs.
41 | */
42 | interface TabConfigurationStrategy {
43 | /**
44 | * Called to configure the tab for the page at the specified position. Typically calls [ ][TabLayout.Tab.setText], but any form of styling can be applied.
45 | *
46 | * @param tab The Tab which should be configured to represent the title of the item at the given
47 | * position in the data set.
48 | * @param position The position of the item within the adapter's data set.
49 | */
50 | fun onConfigureTab(tab: TabLayout.Tab, position: Int)
51 | }
52 |
53 | constructor(
54 | tabLayout: TabLayout,
55 | viewPager: ViewPager2,
56 | tabConfigurationStrategy: TabConfigurationStrategy
57 | ) : this(tabLayout, viewPager, true, tabConfigurationStrategy)
58 |
59 | /**
60 | * Link the TabLayout and the ViewPager2 together. Must be called after ViewPager2 has an adapter
61 | * set. To be called on a new instance of TabLayoutMediator or if the ViewPager2's adapter
62 | * changes.
63 | *
64 | * @throws IllegalStateException If the mediator is already attached, or the ViewPager2 has no
65 | * adapter.
66 | */
67 | fun attach() {
68 | check(!attached) { "TabLayoutMediator is already attached" }
69 | adapter = viewPager.adapter
70 | checkNotNull(adapter) { "TabLayoutMediator attached before ViewPager2 has an " + "adapter" }
71 | attached = true
72 |
73 | // Add our custom OnPageChangeCallback to the ViewPager
74 | onPageChangeCallback = TabLayoutOnPageChangeCallback(tabLayout)
75 | viewPager.registerOnPageChangeCallback(onPageChangeCallback!!)
76 |
77 | // Now we'll add a tab selected listener to set ViewPager's current item
78 | onTabSelectedListener = ViewPagerOnTabSelectedListener(viewPager)
79 | tabLayout.addOnTabSelectedListener(onTabSelectedListener!!)
80 |
81 | // Now we'll populate ourselves from the pager adapter, adding an observer if
82 | // autoRefresh is enabled
83 | if (autoRefresh) {
84 | // Register our observer on the new adapter
85 | pagerAdapterObserver = PagerAdapterObserver()
86 | adapter!!.registerAdapterDataObserver(pagerAdapterObserver!!)
87 | }
88 |
89 | populateTabsFromPagerAdapter()
90 |
91 | // Now update the scroll position to match the ViewPager's current item
92 | tabLayout.setScrollPosition(viewPager.currentItem, 0f, true)
93 | }
94 |
95 | /**
96 | * Unlink the TabLayout and the ViewPager. To be called on a stale TabLayoutMediator if a new one
97 | * is instantiated, to prevent holding on to a view that should be garbage collected. Also to be
98 | * called before [.attach] when a ViewPager2's adapter is changed.
99 | */
100 | fun detach() {
101 | adapter!!.unregisterAdapterDataObserver(pagerAdapterObserver!!)
102 | tabLayout.removeOnTabSelectedListener(onTabSelectedListener!!)
103 | viewPager.unregisterOnPageChangeCallback(onPageChangeCallback!!)
104 | pagerAdapterObserver = null
105 | onTabSelectedListener = null
106 | onPageChangeCallback = null
107 | adapter = null
108 | attached = false
109 | }
110 |
111 | internal fun populateTabsFromPagerAdapter() {
112 | tabLayout.removeAllTabs()
113 |
114 | if (adapter != null) {
115 | val adapterCount = adapter!!.itemCount
116 | for (i in 0 until adapterCount) {
117 | val tab = tabLayout.newTab()
118 | tabConfigurationStrategy.onConfigureTab(tab, i)
119 | tabLayout.addTab(tab, false)
120 | }
121 | // Make sure we reflect the currently set ViewPager item
122 | if (adapterCount > 0) {
123 | val lastItem = tabLayout.tabCount - 1
124 | val currItem = Math.min(viewPager.currentItem, lastItem)
125 | if (currItem != tabLayout.selectedTabPosition) {
126 | tabLayout.selectTab(tabLayout.getTabAt(currItem))
127 | }
128 | }
129 | }
130 | }
131 |
132 | /**
133 | * A [ViewPager2.OnPageChangeCallback] class which contains the necessary calls back to the
134 | * provided [TabLayout] so that the tab position is kept in sync.
135 | *
136 | *
137 | * This class stores the provided TabLayout weakly, meaning that you can use [ ][ViewPager2.registerOnPageChangeCallback] without removing the
138 | * callback and not cause a leak.
139 | */
140 | private class TabLayoutOnPageChangeCallback internal constructor(tabLayout: TabLayout) :
141 | ViewPager2.OnPageChangeCallback() {
142 | private val tabLayoutRef: WeakReference = WeakReference(tabLayout)
143 | private var previousScrollState: Int = 0
144 | private var scrollState: Int = 0
145 |
146 | init {
147 | reset()
148 | }
149 |
150 | override fun onPageScrollStateChanged(state: Int) {
151 | previousScrollState = scrollState
152 | scrollState = state
153 | }
154 |
155 | override fun onPageScrolled(
156 | position: Int,
157 | positionOffset: Float,
158 | positionOffsetPixels: Int
159 | ) {
160 | val tabLayout = tabLayoutRef.get()
161 | if (tabLayout != null) {
162 | // Only update the text selection if we're not settling, or we are settling after
163 | // being dragged
164 | val updateText =
165 | scrollState != SCROLL_STATE_SETTLING || previousScrollState == SCROLL_STATE_DRAGGING
166 | // Update the indicator if we're not settling after being idle. This is caused
167 | // from a setCurrentItem() call and will be handled by an animation from
168 | // onPageSelected() instead.
169 | val updateIndicator =
170 | !(scrollState == SCROLL_STATE_SETTLING && previousScrollState == SCROLL_STATE_IDLE)
171 | tabLayout.setScrollPosition(position, positionOffset, updateText, updateIndicator)
172 | }
173 | }
174 |
175 | override fun onPageSelected(position: Int) {
176 | val tabLayout = tabLayoutRef.get()
177 | if (tabLayout != null
178 | && tabLayout.selectedTabPosition != position
179 | && position < tabLayout.tabCount
180 | ) {
181 | // Select the tab, only updating the indicator if we're not being dragged/settled
182 | // (since onPageScrolled will handle that).
183 | val updateIndicator =
184 | scrollState == SCROLL_STATE_IDLE || scrollState == SCROLL_STATE_SETTLING && previousScrollState == SCROLL_STATE_IDLE
185 | tabLayout.selectTab(tabLayout.getTabAt(position), updateIndicator)
186 | }
187 | }
188 |
189 | internal fun reset() {
190 | scrollState = SCROLL_STATE_IDLE
191 | previousScrollState = scrollState
192 | }
193 | }
194 |
195 | /**
196 | * A [TabLayout.OnTabSelectedListener] class which contains the necessary calls back to the
197 | * provided [ViewPager2] so that the tab position is kept in sync.
198 | */
199 | private class ViewPagerOnTabSelectedListener internal constructor(private val viewPager: ViewPager2) :
200 | TabLayout.OnTabSelectedListener {
201 |
202 | override fun onTabSelected(tab: TabLayout.Tab) {
203 | viewPager.setCurrentItem(tab.position, true)
204 | }
205 |
206 | override fun onTabUnselected(tab: TabLayout.Tab) {
207 | // No-op
208 | }
209 |
210 | override fun onTabReselected(tab: TabLayout.Tab) {
211 | // No-op
212 | }
213 | }
214 |
215 | private inner class PagerAdapterObserver internal constructor() :
216 | RecyclerView.AdapterDataObserver() {
217 |
218 | override fun onChanged() {
219 | populateTabsFromPagerAdapter()
220 | }
221 |
222 | override fun onItemRangeChanged(positionStart: Int, itemCount: Int) {
223 | populateTabsFromPagerAdapter()
224 | }
225 |
226 | override fun onItemRangeChanged(positionStart: Int, itemCount: Int, payload: Any?) {
227 | populateTabsFromPagerAdapter()
228 | }
229 |
230 | override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
231 | populateTabsFromPagerAdapter()
232 | }
233 |
234 | override fun onItemRangeRemoved(positionStart: Int, itemCount: Int) {
235 | populateTabsFromPagerAdapter()
236 | }
237 |
238 | override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) {
239 | populateTabsFromPagerAdapter()
240 | }
241 | }
242 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/instamobile_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instamobile/kotlin-firebase/7d4da154cb0981a19d6add5fe13afffa7f3cdbae/app/src/main/res/drawable-hdpi/instamobile_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/instamobile_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instamobile/kotlin-firebase/7d4da154cb0981a19d6add5fe13afffa7f3cdbae/app/src/main/res/drawable-mdpi/instamobile_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/instamobile_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instamobile/kotlin-firebase/7d4da154cb0981a19d6add5fe13afffa7f3cdbae/app/src/main/res/drawable-xhdpi/instamobile_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/instamobile_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instamobile/kotlin-firebase/7d4da154cb0981a19d6add5fe13afffa7f3cdbae/app/src/main/res/drawable-xxhdpi/instamobile_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/instamobile_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instamobile/kotlin-firebase/7d4da154cb0981a19d6add5fe13afffa7f3cdbae/app/src/main/res/drawable-xxxhdpi/instamobile_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/btn_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/disabled_rounded_blue_button.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_blue_button.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_camera.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_chat.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_fast.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_firebase.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_kotlin.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_logout.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_save_time.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_white_button.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/launch_screen.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 | -
6 |
7 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/placeholder.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instamobile/kotlin-firebase/7d4da154cb0981a19d6add5fe13afffa7f3cdbae/app/src/main/res/drawable/placeholder.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable/rounded_blue_button.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/rounded_border.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
9 |
10 |
11 |
12 |
13 |
14 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/sign_up_button_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/tab_indicator_default.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/tab_indicator_selected.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/tab_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_host.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
14 |
15 |
23 |
24 |
29 |
30 |
31 |
32 |
43 |
44 |
45 |
46 |
55 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/drawer_header_layout.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
20 |
21 |
32 |
33 |
51 |
52 |
66 |
67 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_auth.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
10 |
11 |
12 |
17 |
18 |
33 |
34 |
52 |
53 |
69 |
70 |
86 |
87 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_home.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_login.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
10 |
11 |
14 |
15 |
19 |
20 |
33 |
34 |
40 |
41 |
42 |
43 |
58 |
59 |
74 |
75 |
90 |
91 |
105 |
106 |
123 |
124 |
138 |
139 |
155 |
156 |
170 |
171 |
172 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_on_boarding.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
10 |
11 |
12 |
17 |
18 |
31 |
32 |
40 |
41 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_sign_up.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
7 |
10 |
11 |
12 |
16 |
17 |
20 |
21 |
34 |
35 |
41 |
42 |
43 |
44 |
45 |
59 |
60 |
72 |
73 |
81 |
82 |
97 |
98 |
113 |
114 |
129 |
130 |
145 |
146 |
161 |
162 |
182 |
183 |
184 |
185 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/slide.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
11 |
12 |
13 |
16 |
17 |
34 |
35 |
54 |
55 |
72 |
73 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/drawer_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instamobile/kotlin-firebase/7d4da154cb0981a19d6add5fe13afffa7f3cdbae/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instamobile/kotlin-firebase/7d4da154cb0981a19d6add5fe13afffa7f3cdbae/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instamobile/kotlin-firebase/7d4da154cb0981a19d6add5fe13afffa7f3cdbae/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instamobile/kotlin-firebase/7d4da154cb0981a19d6add5fe13afffa7f3cdbae/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instamobile/kotlin-firebase/7d4da154cb0981a19d6add5fe13afffa7f3cdbae/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instamobile/kotlin-firebase/7d4da154cb0981a19d6add5fe13afffa7f3cdbae/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instamobile/kotlin-firebase/7d4da154cb0981a19d6add5fe13afffa7f3cdbae/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instamobile/kotlin-firebase/7d4da154cb0981a19d6add5fe13afffa7f3cdbae/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instamobile/kotlin-firebase/7d4da154cb0981a19d6add5fe13afffa7f3cdbae/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/instamobile/kotlin-firebase/7d4da154cb0981a19d6add5fe13afffa7f3cdbae/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/navigation/main_graph.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
16 |
21 |
30 |
31 |
36 |
43 |
50 |
51 |
56 |
61 |
66 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #7F70E4
4 | #7F70E4
5 | #7F70E4
6 |
7 | #7e7e7e
8 | #ffffff
9 | #d6d6d6
10 | #000000
11 |
12 | #66000000
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Instakotlin Starter Kit
3 | Continue
4 |
5 |
6 | Use this starter kit to build your Android app faster.
7 | Log In
8 | Sign Up
9 | Kotlin Starter Kit with Firebase
10 |
11 | Facebook Login
12 | 686078505161575
13 | fb686078505161575
14 |
15 | Full Name
16 | E-mail
17 | Password
18 | Phone Number
19 | or
20 | Sign in
21 | Create new account
22 | Name is required to sign up.
23 | Phone number is required to sign up.
24 | Email address is required to sign up.
25 | Password is required to sign up.
26 | This phone number is badly formatted.
27 | This email address is badly formatted.
28 | Password should be at least 6 characters.
29 | Select Image
30 | Log Out
31 |
32 |
33 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
19 |
20 |
26 |
29 |
30 |
36 |
37 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/test/java/com/instamobile/ui/fragment/onBoarding/walkthroughactivity/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.instamobile.ui.fragment.onBoarding.walkthroughactivity
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | /**
7 | * Example local unit test, which will execute on the development machine (host).
8 | *
9 | * See [testing documentation](http://d.android.com/tools/testing).
10 | */
11 | class ExampleUnitTest {
12 | @Test
13 | fun addition_isCorrect() {
14 | assertEquals(4, 2 + 2)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | ext.kotlin_version = '1.3.50'
5 | repositories {
6 | google()
7 | jcenter()
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:3.5.2'
11 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
12 | classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0"
13 | classpath 'com.google.gms:google-services:4.3.2'
14 |
15 | // NOTE: Do not place your application dependencies here; they belong
16 | // in the individual module build.gradle files
17 | }
18 | }
19 |
20 | allprojects {
21 | repositories {
22 | google()
23 | jcenter()
24 | }
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
30 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
22 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Oct 21 18:29:59 EET 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/local.properties:
--------------------------------------------------------------------------------
1 | ## This file must *NOT* be checked into Version Control Systems,
2 | # as it contains information specific to your local configuration.
3 | #
4 | # Location of the SDK. This is only used by Gradle.
5 | # For customization when using a Version Control System, please read the
6 | # header note.
7 | #Tue Oct 22 18:40:47 PDT 2019
8 | sdk.dir=/Users/florian/Library/Android/sdk
9 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 | rootProject.name='WalkthroughActivity'
3 |
--------------------------------------------------------------------------------