diff options
author | Allan Wang <me@allanwang.ca> | 2017-05-30 01:03:01 -0700 |
---|---|---|
committer | Allan Wang <me@allanwang.ca> | 2017-05-30 01:03:01 -0700 |
commit | 4c44dbc9933bd726c1da0bf326102835c4974d6b (patch) | |
tree | 18be2954a53c292eef132f9a3dc630c4071c7a9b /app/src/main | |
parent | 461425eb6054f18cea1990a4117fe8c78e888ddf (diff) | |
download | frost-4c44dbc9933bd726c1da0bf326102835c4974d6b.tar.gz frost-4c44dbc9933bd726c1da0bf326102835c4974d6b.tar.bz2 frost-4c44dbc9933bd726c1da0bf326102835c4974d6b.zip |
create retrofacebook and token retrieval
Diffstat (limited to 'app/src/main')
17 files changed, 374 insertions, 23 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 033b8d79..ff98de24 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" package="com.pitchedapps.frost"> <!-- To auto-complete the email text field in the login form with the user's emails --> @@ -18,12 +19,12 @@ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <application + android:name=".FrostApp" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" - android:name=".FrostApp" android:theme="@style/AppTheme"> <activity android:name=".StartActivity" @@ -91,6 +92,37 @@ android:scheme="https" /> </intent-filter> </activity> + <activity android:name=".LoginActivity" /> + <activity + android:name="com.facebook.FacebookActivity" + android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation" + android:label="@string/app_name" + android:theme="@android:style/Theme.Translucent.NoTitleBar" + tools:replace="android:theme" /> + <activity + android:name="com.facebook.CustomTabActivity" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.VIEW" /> + + <category android:name="android.intent.category.DEFAULT" /> + <category android:name="android.intent.category.BROWSABLE" /> + + <data android:scheme="@string/fb_login_protocol_scheme" /> + </intent-filter> + </activity> + + <meta-data + android:name="com.facebook.sdk.ApplicationId" + android:value="@string/facebook_app_id" /> + <meta-data + android:name="com.facebook.sdk.ApplicationName" + android:value="@string/facebook_app_name" /> + + <provider + android:name="com.facebook.FacebookContentProvider" + android:authorities="@string/facebook_authorities" + android:exported="true" /> </application> </manifest>
\ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/FbActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/FbActivity.kt new file mode 100644 index 00000000..0132a4db --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/FbActivity.kt @@ -0,0 +1,21 @@ +package com.pitchedapps.frost + +import android.os.Bundle +import android.support.annotation.CallSuper +import android.support.v7.app.AppCompatActivity +import com.facebook.AccessToken +import com.pitchedapps.frost.utils.L + +/** + * Created by Allan Wang on 2017-05-29. + */ +open class FbActivity : AppCompatActivity() { + var accessToken: AccessToken? = null + + @CallSuper + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + accessToken = AccessToken.getCurrentAccessToken() + L.e("Access ${accessToken?.token}") + } +}
\ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt b/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt index e050f285..0fb4e7db 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/FrostApp.kt @@ -1,6 +1,8 @@ package com.pitchedapps.frost import android.app.Application +import com.pitchedapps.frost.facebook.retro.FrostApi +import com.pitchedapps.frost.facebook.retro.IFrost import com.pitchedapps.frost.utils.CrashReportingTree import com.pitchedapps.frost.utils.Prefs import io.realm.Realm @@ -13,15 +15,11 @@ import timber.log.Timber.DebugTree */ class FrostApp : Application() { - companion object { - lateinit var prefs: Prefs - } - override fun onCreate() { if (BuildConfig.DEBUG) Timber.plant(DebugTree()) else Timber.plant(CrashReportingTree()) - - prefs = Prefs(applicationContext) + Prefs(applicationContext) + FrostApi(applicationContext) Realm.init(applicationContext) super.onCreate() } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/LoginActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/LoginActivity.kt new file mode 100644 index 00000000..8ef60991 --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/LoginActivity.kt @@ -0,0 +1,64 @@ +package com.pitchedapps.frost + +import android.content.Intent +import android.content.pm.PackageInstaller +import android.os.Bundle +import android.widget.Button +import com.facebook.CallbackManager +import com.facebook.FacebookCallback +import com.facebook.FacebookException +import com.facebook.login.LoginBehavior +import com.facebook.login.LoginManager +import com.facebook.login.LoginResult +import com.facebook.login.widget.LoginButton +import com.pitchedapps.frost.utils.L +import java.util.* + + +/** + * Created by Allan Wang on 2017-05-29. + */ +class LoginActivity : FbActivity() { + lateinit var callback: CallbackManager + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.login_teest) + val loginButton = findViewById(R.id.login_button) as LoginButton + loginButton.loginBehavior = LoginBehavior.WEB_VIEW_ONLY + loginButton.setReadPermissions("email") + val switchh = findViewById(R.id.switchh) as Button + switchh.setOnClickListener { + startActivity(Intent(this, MainActivity::class.java)) + finish() + } + // If using in a fragment +// loginButton.setFragment(this) + // Other app specific specialization + + // Callback registration + callback = CallbackManager.Factory.create() + loginButton.registerCallback(callback, object : FacebookCallback<LoginResult> { + override fun onSuccess(loginResult: LoginResult) { + L.e("Success") + L.e("Success ${loginResult.accessToken.token}") + } + + override fun onCancel() { + // App code + L.e("Cancel") + } + + override fun onError(exception: FacebookException) { + // App code + L.e("Error") + } + }) + +// LoginManager.getInstance().logInWithReadPermissions(this, Arrays.asList("public_profile")); + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + callback.onActivityResult(requestCode, resultCode, data) + } +}
\ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt index 0db5ee72..deb4f5d6 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/MainActivity.kt @@ -1,5 +1,6 @@ package com.pitchedapps.frost +import android.content.Intent import android.os.Bundle import android.support.design.widget.FloatingActionButton import android.support.design.widget.Snackbar @@ -69,7 +70,10 @@ class MainActivity : AppCompatActivity(), KeyPairObservable { // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. when (item.itemId) { - R.id.action_settings -> return true + R.id.action_settings -> { + startActivity(Intent(this, LoginActivity::class.java)) + finish() + } R.id.action_changelog -> Changelog.show(this) else -> return super.onOptionsItemSelected(item) } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt b/app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt index e8c65be3..390efb1a 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/StartActivity.kt @@ -11,7 +11,7 @@ class StartActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - startActivity(Intent(this, MainActivity::class.java)) + startActivity(Intent(this, LoginActivity::class.java)) finish() } }
\ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbToken.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbToken.kt new file mode 100644 index 00000000..22dc25f7 --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/FbToken.kt @@ -0,0 +1,13 @@ +package com.pitchedapps.frost.facebook + +import com.facebook.AccessToken + +/** + * Created by Allan Wang on 2017-05-30. + */ +val token: String? + get() = AccessToken.getCurrentAccessToken()?.token + +fun setToken() { + +}
\ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/UrlData.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/UrlData.kt index 80972050..d5f9db6e 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/facebook/UrlData.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/UrlData.kt @@ -18,6 +18,7 @@ import io.realm.annotations.PrimaryKey * Created by Allan Wang on 2017-05-29. */ enum class FbUrl(@StringRes val titleId: Int, val icon: IIcon, val url: String) { + LOGIN(R.string.feed, CommunityMaterial.Icon.cmd_newspaper, "https://www.facebook.com/v2.9/dialog/oauth?client_id=$FB_KEY&redirect_uri=https://touch.facebook.com/&response_type=token,granted_scopes"), FEED(R.string.feed, CommunityMaterial.Icon.cmd_newspaper, "https://touch.facebook.com/"), PROFILE(R.string.profile, CommunityMaterial.Icon.cmd_account, "https://touch.facebook.com/me/"), EVENTS(R.string.events, GoogleMaterial.Icon.gmd_event, "https://touch.facebook.com/events/upcoming"), @@ -60,6 +61,6 @@ fun loadFbTab(c: Context): List<FbTab> { val realmList = mutableListOf<FbTabRealm>() realm(RealmFiles.TABS, Realm.Transaction { it.copyFromRealm(realmList) }) if (realmList.isNotEmpty()) return realmList.map { FbTab(it) } - return FbUrl.values().map { it.tabInfo(c) } + return listOf(FbUrl.FEED, FbUrl.MESSAGES, FbUrl.NOTIFICATIONS).map { it.tabInfo(c) } } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/FrostApi.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/FrostApi.kt new file mode 100644 index 00000000..746bf0df --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/FrostApi.kt @@ -0,0 +1,52 @@ +package com.pitchedapps.frost.facebook.retro + +import android.content.Context +import com.facebook.stetho.okhttp3.StethoInterceptor +import com.google.gson.GsonBuilder +import com.pitchedapps.frost.BuildConfig +import io.reactivex.schedulers.Schedulers +import okhttp3.Cache +import okhttp3.OkHttpClient +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory +import retrofit2.converter.gson.GsonConverterFactory +import java.io.File + +/** + * Created by Allan Wang on 2017-05-30. + * + * API for data retrieval + */ +object FrostApi { + + internal lateinit var frostApi: IFrost + + operator fun invoke(context: Context) { + val cacheDir = File(context.cacheDir, "responses") + val cacheSize = 5L * 1024 * 1024 //10MiB + val cache = Cache(cacheDir, cacheSize) + + val client = OkHttpClient.Builder() + .addInterceptor(FrostInterceptor(context)) + .cache(cache) + + + //add logger and stetho last + + if (BuildConfig.DEBUG || BuildConfig.BUILD_TYPE == "releaseTest") { //log if not full release + client.addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BASIC)) + client.addNetworkInterceptor(StethoInterceptor()) + } + + val gson = GsonBuilder().setLenient() + + val retrofit = Retrofit.Builder() + .baseUrl("https://graph.facebook.com/") + .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io())) + .addConverterFactory(GsonConverterFactory.create(gson.create())) + .client(client.build()) + .build(); + frostApi = retrofit.create(IFrost::class.java) + } +}
\ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/FrostData.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/FrostData.kt new file mode 100644 index 00000000..07d686a7 --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/FrostData.kt @@ -0,0 +1,8 @@ +package com.pitchedapps.frost.facebook.retro + +/** + * Created by Allan Wang on 2017-05-30. + * + * Collection of Graph API outputs + */ +data class Me(val name: String, val id: String)
\ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/FrostInterceptor.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/FrostInterceptor.kt new file mode 100644 index 00000000..f745aedf --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/FrostInterceptor.kt @@ -0,0 +1,29 @@ +package com.pitchedapps.frost.facebook.retro + +import android.content.Context +import com.pitchedapps.frost.facebook.token +import com.pitchedapps.frost.utils.Utils +import okhttp3.Interceptor +import okhttp3.Response + +/** + * Created by Allan Wang on 2017-05-30. + */ +private val maxStale = 60 * 60 * 24 * 28 //maxAge to get from cache if online (4 weeks) +const val ACCESS_TOKEN = "access_token" + +class FrostInterceptor(val context: Context) : Interceptor { + + override fun intercept(chain: Interceptor.Chain): Response? { + val request = chain.request() + val requestBuilder = request.newBuilder() + val urlBase = request.url() + val urlWithToken = urlBase.newBuilder() + if (urlBase.queryParameter(ACCESS_TOKEN) == null && token != null) + urlWithToken.addQueryParameter(ACCESS_TOKEN, token) + requestBuilder.url(urlWithToken.build()) + if (!Utils.isNetworkAvailable(context)) requestBuilder.addHeader("Cache-Control", "public, only-if-cached, max-stale=" + maxStale) + return chain.proceed(requestBuilder.build()) + } + +}
\ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/IFrost.kt b/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/IFrost.kt new file mode 100644 index 00000000..6c50fa74 --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/facebook/retro/IFrost.kt @@ -0,0 +1,16 @@ +package com.pitchedapps.frost.facebook.retro + +import com.pitchedapps.frost.facebook.token +import retrofit2.Call +import retrofit2.http.GET +import retrofit2.http.Query + +/** + * Created by Allan Wang on 2017-05-30. + */ +interface IFrost { + + @GET("me") + fun me(@Query(ACCESS_TOKEN) accessToken: String? = token): Call<Me> + +}
\ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt index fc2e9d1c..ad222145 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Prefs.kt @@ -1,30 +1,36 @@ package com.pitchedapps.frost.utils -import com.pitchedapps.frost.FrostApp +import android.content.Context +import android.content.SharedPreferences /** * Created by Allan Wang on 2017-05-28. */ -val prefs: Prefs by lazy { FrostApp.prefs } -class Prefs(c: android.content.Context) { - private companion object { - val PREFERENCE_NAME = "${com.pitchedapps.frost.BuildConfig.APPLICATION_ID}.prefs" - val LAST_ACTIVE = "last_active" +private val PREFERENCE_NAME = "${com.pitchedapps.frost.BuildConfig.APPLICATION_ID}.prefs" +private val LAST_ACTIVE = "last_active" + +object Prefs { + + val prefs: Prefs by lazy { this } + + lateinit private var c: Context + operator fun invoke(c: Context) { + this.c = c } - private val prefs: android.content.SharedPreferences by lazy { c.getSharedPreferences(com.pitchedapps.frost.utils.Prefs.Companion.PREFERENCE_NAME, android.content.Context.MODE_PRIVATE) } + private val sp: SharedPreferences by lazy { c.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE) } var lastActive: Long - get() = prefs.getLong(com.pitchedapps.frost.utils.Prefs.Companion.LAST_ACTIVE, -1) - set(value) = set(com.pitchedapps.frost.utils.Prefs.Companion.LAST_ACTIVE, System.currentTimeMillis()) + get() = sp.getLong(LAST_ACTIVE, -1) + set(value) = set(LAST_ACTIVE, System.currentTimeMillis()) init { lastActive = 0 } - private fun set(key: String, value: Boolean) = prefs.edit().putBoolean(key, value).apply() - private fun set(key: String, value: Int) = prefs.edit().putInt(key, value).apply() - private fun set(key: String, value: Long) = prefs.edit().putLong(key, value).apply() - private fun set(key: String, value: String) = prefs.edit().putString(key, value).apply() + private fun set(key: String, value: Boolean) = sp.edit().putBoolean(key, value).apply() + private fun set(key: String, value: Int) = sp.edit().putInt(key, value).apply() + private fun set(key: String, value: Long) = sp.edit().putLong(key, value).apply() + private fun set(key: String, value: String) = sp.edit().putString(key, value).apply() }
\ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt b/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt index bbf0e1f0..d5bb2502 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/utils/Utils.kt @@ -1,6 +1,8 @@ package com.pitchedapps.frost.utils +import android.content.Context import android.content.res.Resources +import android.net.ConnectivityManager /** * Created by Allan Wang on 2017-05-28. @@ -8,4 +10,10 @@ import android.content.res.Resources object Utils { fun dpToPx(dp: Int) = (dp * android.content.res.Resources.getSystem().displayMetrics.density).toInt() fun pxToDp(px:Int) = (px / android.content.res.Resources.getSystem().displayMetrics.density).toInt() + + fun isNetworkAvailable(context: Context): Boolean { + val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager + val activeNetworkInfo = connectivityManager.activeNetworkInfo + return activeNetworkInfo != null && activeNetworkInfo.isConnectedOrConnecting + } }
\ No newline at end of file diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostWebView.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostWebView.kt index 62115276..f09887d8 100644 --- a/app/src/main/kotlin/com/pitchedapps/frost/views/FrostWebView.kt +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/FrostWebView.kt @@ -68,6 +68,7 @@ class FrostWebView @JvmOverloads constructor( super.onPageFinished(view, url) observable.onNext(WebStatus.LOADED) // CookieManager.getInstance().flush() + L.d("Loaded $url") } }) } diff --git a/app/src/main/kotlin/com/pitchedapps/frost/views/LoginWebView.kt b/app/src/main/kotlin/com/pitchedapps/frost/views/LoginWebView.kt new file mode 100644 index 00000000..7d1948fe --- /dev/null +++ b/app/src/main/kotlin/com/pitchedapps/frost/views/LoginWebView.kt @@ -0,0 +1,78 @@ +package com.pitchedapps.frost.views + +import android.annotation.SuppressLint +import android.content.Context +import android.graphics.Bitmap +import android.net.UrlQuerySanitizer +import android.util.AttributeSet +import android.view.View +import android.webkit.WebResourceError +import android.webkit.WebResourceRequest +import android.webkit.WebView +import android.webkit.WebViewClient +import com.facebook.AccessToken +import com.pitchedapps.frost.facebook.FB_KEY +import com.pitchedapps.frost.facebook.retro.FrostApi.frostApi +import com.pitchedapps.frost.facebook.retro.Me +import com.pitchedapps.frost.utils.L +import retrofit2.Call +import retrofit2.Callback +import retrofit2.Response + +/** + * Created by Allan Wang on 2017-05-29. + */ +class LoginWebView @JvmOverloads constructor( + context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 +) : WebView(context, attrs, defStyleAttr) { + + init { + setupWebview() + } + + @SuppressLint("SetJavaScriptEnabled") + fun setupWebview() { + settings.javaScriptEnabled = true + setLayerType(View.LAYER_TYPE_HARDWARE, null) + setWebViewClient(object : WebViewClient() { + override fun onReceivedError(view: WebView?, request: WebResourceRequest?, error: WebResourceError?) { + super.onReceivedError(view, request, error) + L.e("Error ${request}") + } + + override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) { + super.onPageStarted(view, url, favicon) + L.d("Loading $url") + } + + override fun onPageFinished(view: WebView?, url: String?) { + super.onPageFinished(view, url) + if (url == null) return + val sanitizer = UrlQuerySanitizer(url) + val accessToken = sanitizer.getValue("access_token") + val expiresIn = sanitizer.getValue("expires_in") + val grantedScopes = sanitizer.getValue("granted_scopes") + val deniedScopes = sanitizer.getValue("deniedScopes") + + + L.d("Loaded $url") + } + }) + } + + fun saveAccessToken(accessToken: String, expiresIn: String, grantedScopes: String?, deniedScopes: String?) { + L.d("Granted $grantedScopes") + L.d("Denied $deniedScopes") + frostApi.me(accessToken).enqueue(object : Callback<Me> { + override fun onFailure(call: Call<Me>?, t: Throwable?) { + TODO("not implemented") //To change body of created functions use File | Settings | File Templates. + } + + override fun onResponse(call: Call<Me>, response: Response<Me>) { + AccessToken.setCurrentAccessToken(AccessToken(accessToken, FB_KEY.toString(), response.body().id, null, null, null, null, null)) + } + }) + + } + +} diff --git a/app/src/main/res/layout/login_teest.xml b/app/src/main/res/layout/login_teest.xml new file mode 100644 index 00000000..47e72acd --- /dev/null +++ b/app/src/main/res/layout/login_teest.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + + <com.facebook.login.widget.LoginButton + android:id="@+id/login_button" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_horizontal" + android:layout_marginBottom="30dp" + android:layout_marginTop="30dp" /> + + <Button + android:id="@+id/switchh" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="switch" /> +</LinearLayout>
\ No newline at end of file |