Finish authentication
This commit is contained in:
parent
b214238457
commit
33f0711b1f
@ -51,6 +51,7 @@ dependencies {
|
||||
testImplementation 'junit:junit:4.13'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||
implementation 'com.google.android.material:material:1.1.0'
|
||||
}
|
||||
|
||||
|
||||
@ -69,9 +70,13 @@ task jacocoTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest', 'crea
|
||||
def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*']
|
||||
def kotlinDebugTree = fileTree(dir: "$project.buildDir/tmp/kotlin-classes/debug", excludes: fileFilter)
|
||||
def mainSrc = "$project.projectDir/src/main/java"
|
||||
sourceDirectories = files([mainSrc])
|
||||
classDirectories = files([kotlinDebugTree])
|
||||
executionData = fileTree(dir: project.buildDir, includes: [
|
||||
'jacoco/testDebugUnitTest.exec', 'outputs/code-coverage/debugAndroidTest/connected/*coverage.ec'
|
||||
])
|
||||
getSourceDirectories().from(files([mainSrc]))
|
||||
getClassDirectories().from(files([kotlinDebugTree]))
|
||||
getExecutionData().from(fileTree(dir: project.buildDir, includes: [
|
||||
|
||||
'outputs/code_coverage/debugAndroidTest/connected/*coverage.ec',
|
||||
|
||||
'jacoco/testDebugUnitTest.exec'
|
||||
|
||||
]))
|
||||
}
|
@ -9,8 +9,19 @@
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity android:name=".LoginActivity"></activity>
|
||||
<activity android:name=".MainActivity">
|
||||
<activity
|
||||
android:name=".LoginActivity"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<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:host="${applicationId}"
|
||||
android:scheme="oauth2redirect" />
|
||||
</intent-filter>
|
||||
</activity> <activity android:name=".MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
|
@ -2,88 +2,182 @@ package com.h.pixeldroid
|
||||
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import com.h.pixeldroid.api.PixelfedAPI
|
||||
import com.h.pixeldroid.objects.Application
|
||||
import com.h.pixeldroid.objects.Token
|
||||
import kotlinx.android.synthetic.main.activity_login.*
|
||||
import okhttp3.HttpUrl
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
|
||||
class LoginActivity : AppCompatActivity() {
|
||||
|
||||
private val OAUTH_SCHEME = "oauth2redirect"
|
||||
private val PACKAGE_ID = "com.h.pixeldroid"
|
||||
private val SCOPE = "read write follow"
|
||||
private val APP_NAME = "PixelDroid"
|
||||
private lateinit var preferences: SharedPreferences
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_login)
|
||||
|
||||
connect_instance_button.setOnClickListener { onClickConnect() }
|
||||
|
||||
preferences = getSharedPreferences(
|
||||
"$PACKAGE_ID.pref", Context.MODE_PRIVATE
|
||||
)
|
||||
|
||||
|
||||
}
|
||||
|
||||
fun connexion(view: View) {
|
||||
override fun onStart(){
|
||||
super.onStart()
|
||||
|
||||
val url = intent.data
|
||||
|
||||
if (url != null && url.toString().startsWith("$OAUTH_SCHEME://$PACKAGE_ID")) {
|
||||
|
||||
val code = url.getQueryParameter("code")
|
||||
val error = url.getQueryParameter("error")
|
||||
|
||||
// Restore previous values from preferences
|
||||
val domain = preferences.getString("domain", "")
|
||||
val clientId = preferences.getString("clientID", "")
|
||||
val clientSecret = preferences.getString("clientSecret", "")
|
||||
|
||||
if (code != null && !domain.isNullOrEmpty() && !clientId.isNullOrEmpty() && !clientSecret.isNullOrEmpty()) {
|
||||
//Successful authorization
|
||||
val callback = object : Callback<Token> {
|
||||
override fun onResponse(call: Call<Token>, response: Response<Token>) {
|
||||
if (response.isSuccessful) {
|
||||
authenticationSuccessful(domain, response.body()?.access_token)
|
||||
} else {
|
||||
failedRegistration("Error getting token")
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<Token>, t: Throwable) {
|
||||
failedRegistration("Error getting token")
|
||||
}
|
||||
}
|
||||
|
||||
val pixelfedAPI = PixelfedAPI.create("https://$domain")
|
||||
pixelfedAPI.obtainToken(clientId, clientSecret, "$OAUTH_SCHEME://$PACKAGE_ID", SCOPE, code,
|
||||
"authorization_code").enqueue(callback)
|
||||
} else if (error != null) {
|
||||
failedRegistration("Authentication denied")
|
||||
} else {
|
||||
failedRegistration("Unknown response")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun authenticationSuccessful(domain: String, accessToken: String?) {
|
||||
|
||||
Log.e("Token", accessToken!!)
|
||||
val intent = Intent(this, MainActivity::class.java)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun onClickConnect() {
|
||||
|
||||
connect_instance_button.isEnabled = false
|
||||
|
||||
val domain = editText.text.toString()
|
||||
|
||||
val normalizedDomain = normalizeDomain(domain)
|
||||
try{
|
||||
HttpUrl.Builder().host(domain).scheme("https").build()
|
||||
} catch (e: IllegalArgumentException) {
|
||||
failedRegistration("Invalid domain")
|
||||
return
|
||||
}
|
||||
|
||||
preferences.edit()
|
||||
.putString("domain", normalizedDomain)
|
||||
.apply()
|
||||
|
||||
val callback = object : Callback<Application> {
|
||||
override fun onResponse(call: Call<Application>, response: Response<Application>) {
|
||||
if (!response.isSuccessful) {
|
||||
// TODO
|
||||
failedRegistration()
|
||||
return
|
||||
}
|
||||
|
||||
val credentials = response.body()
|
||||
val clientId = credentials!!.client_id
|
||||
val clientId = credentials?.client_id ?: return failedRegistration()
|
||||
val clientSecret = credentials.client_secret
|
||||
val domain = editText.text.toString()
|
||||
|
||||
preferences = getSharedPreferences(
|
||||
"com.h.PixelDroid.pref", Context.MODE_PRIVATE
|
||||
)
|
||||
preferences.edit().putString("clientID", clientId)
|
||||
|
||||
preferences.edit()
|
||||
.putString("clientID", clientId)
|
||||
.putString("clientSecret", clientSecret)
|
||||
.putString("domain", domain)
|
||||
.apply()
|
||||
|
||||
if (clientId != null) {
|
||||
redirect(domain, clientId)
|
||||
}
|
||||
redirect(normalizedDomain, clientId)
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<Application>, t: Throwable) {
|
||||
failedRegistration()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
val api = PixelfedAPI.create("https://pixelfed.de")
|
||||
api.registerApplication(
|
||||
"pixeldroid",
|
||||
"oauth2redirect://com.h.pixeldroid", "read write follow"
|
||||
val pixelfedAPI = PixelfedAPI.create("https://$normalizedDomain")
|
||||
pixelfedAPI.registerApplication(
|
||||
APP_NAME,
|
||||
"$OAUTH_SCHEME://$PACKAGE_ID", SCOPE
|
||||
)
|
||||
.enqueue(callback)
|
||||
|
||||
}
|
||||
|
||||
fun redirect(domain: String, client_id: String) {
|
||||
private fun failedRegistration(message: String =
|
||||
"Could not register the application with this server"){
|
||||
connect_instance_button.isEnabled = true
|
||||
editText.error = message
|
||||
}
|
||||
private fun normalizeDomain(domain: String): String {
|
||||
var d = domain.replace("http://", "")
|
||||
d = d.replace("https://", "")
|
||||
return d.trim(Char::isWhitespace)
|
||||
}
|
||||
|
||||
val url = "https://" + domain + "/oauth/authorize" + "?" +
|
||||
fun redirect(normalizedDomain: String, client_id: String) {
|
||||
|
||||
val url = "https://$normalizedDomain/oauth/authorize?" +
|
||||
"client_id" + "=" + client_id + "&" +
|
||||
"redirect_uri" + "=" + "oauth2redirect://com.h.pixeldroid" + "&" +
|
||||
"redirect_uri" + "=" + "$OAUTH_SCHEME://$PACKAGE_ID" + "&" +
|
||||
"response_type=code" + "&" +
|
||||
"scope=read write follow"
|
||||
"scope=$SCOPE"
|
||||
|
||||
browser(this, url)
|
||||
}
|
||||
|
||||
fun browser(context: Context, url: String) {
|
||||
private fun browser(context: Context, url: String) {
|
||||
val intent = CustomTabsIntent.Builder().build()
|
||||
|
||||
|
||||
try {
|
||||
intent.launchUrl(context, Uri.parse(url))
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
Log.w("login", "Could not launch browser")
|
||||
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
|
||||
if (browserIntent.resolveActivity(packageManager) != null) {
|
||||
startActivity(browserIntent)
|
||||
} else {
|
||||
failedRegistration(message="Could not launch a browser, do you have one?")
|
||||
return
|
||||
}
|
||||
}
|
||||
connect_instance_button.isEnabled = true
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package com.h.pixeldroid.api
|
||||
|
||||
import com.h.pixeldroid.objects.Application
|
||||
import com.h.pixeldroid.objects.Status
|
||||
import com.h.pixeldroid.objects.Token
|
||||
import retrofit2.Call
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
|
||||
@ -31,7 +32,18 @@ interface PixelfedAPI {
|
||||
@Field("website") website: String? = null
|
||||
): Call<Application>
|
||||
|
||||
@GET("/api/v1/timelines/public")
|
||||
@FormUrlEncoded
|
||||
@POST("/oauth/token")
|
||||
fun obtainToken(
|
||||
@Field("client_id") client_id: String,
|
||||
@Field("client_secret") client_secret: String,
|
||||
@Field("redirect_uri") redirect_uri: String,
|
||||
@Field("scope") scope: String? = "read",
|
||||
@Field("code") code: String? = null,
|
||||
@Field("grant_type") grant_type: String? = null
|
||||
): Call<Token>
|
||||
|
||||
@GET("/api/v1/timelines/public")
|
||||
fun timelinePublic(
|
||||
@Query("local") local: Boolean? = null,
|
||||
@Query("max_id") max_id: String? = null,
|
||||
|
@ -7,33 +7,33 @@
|
||||
tools:context=".LoginActivity">
|
||||
|
||||
<Button
|
||||
android:id="@+id/button"
|
||||
android:id="@+id/connect_instance_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="162dp"
|
||||
android:layout_marginEnd="161dp"
|
||||
android:layout_marginBottom="425dp"
|
||||
android:onClick="connexion"
|
||||
android:layout_marginTop="36dp"
|
||||
android:text="Connect"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
app:layout_constraintHorizontal_bias="0.498"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/domainTextInputLayout" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/editText"
|
||||
android:layout_width="wrap_content"
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/domainTextInputLayout"
|
||||
android:layout_width="250dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="99dp"
|
||||
android:layout_marginTop="113dp"
|
||||
android:layout_marginEnd="99dp"
|
||||
android:layout_marginBottom="100dp"
|
||||
android:ems="10"
|
||||
android:inputType="textPersonName"
|
||||
android:text="pixelfed.de"
|
||||
app:layout_constraintBottom_toTopOf="@+id/button"
|
||||
android:hint="Domain of your instance"
|
||||
app:errorEnabled="true"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:autofillHints=""
|
||||
tools:ignore="LabelFor" />
|
||||
app:layout_constraintVertical_bias="0.40">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/editText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:inputType="textUri" />
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
Loading…
x
Reference in New Issue
Block a user