Use nodeinfo endpoint to get info about the capabilities of the instance
This commit is contained in:
parent
bd39bc681d
commit
7bca413d60
|
@ -22,7 +22,7 @@ test:
|
||||||
- adb shell settings put global transition_animation_scale 0.0
|
- adb shell settings put global transition_animation_scale 0.0
|
||||||
- adb shell settings put global animator_duration_scale 0.0
|
- adb shell settings put global animator_duration_scale 0.0
|
||||||
|
|
||||||
- ./gradlew build connectedCheck jacocoTestReport
|
- ./gradlew build connectedCheck connectedDebugAndroidTest jacocoTestReport
|
||||||
|
|
||||||
- cat app/build/reports/jacoco/jacocoTestReport/html/index.html | grep -o 'Total[^%]*%'
|
- cat app/build/reports/jacoco/jacocoTestReport/html/index.html | grep -o 'Total[^%]*%'
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ android {
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||||
implementation 'androidx.core:core-ktx:1.3.1'
|
implementation 'androidx.core:core-ktx:1.3.1'
|
||||||
implementation 'androidx.preference:preference:1.1.1'
|
implementation 'androidx.preference:preference:1.1.1'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||||
|
@ -68,7 +68,7 @@ dependencies {
|
||||||
implementation 'io.reactivex.rxjava2:rxjava:2.2.17'
|
implementation 'io.reactivex.rxjava2:rxjava:2.2.17'
|
||||||
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
|
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
|
||||||
implementation "androidx.browser:browser:1.2.0"
|
implementation "androidx.browser:browser:1.2.0"
|
||||||
implementation 'com.google.android.material:material:1.1.0'
|
implementation 'com.google.android.material:material:1.2.0'
|
||||||
implementation 'com.github.connyduck:sparkbutton:4.0.0'
|
implementation 'com.github.connyduck:sparkbutton:4.0.0'
|
||||||
|
|
||||||
def room_version = "2.2.5"
|
def room_version = "2.2.5"
|
||||||
|
@ -140,14 +140,14 @@ dependencies {
|
||||||
debugImplementation "androidx.fragment:fragment-testing:$fragment_version"
|
debugImplementation "androidx.fragment:fragment-testing:$fragment_version"
|
||||||
|
|
||||||
// Use the most recent version of CameraX
|
// Use the most recent version of CameraX
|
||||||
def camerax_version = '1.0.0-beta07'
|
def camerax_version = '1.0.0-beta08'
|
||||||
implementation "androidx.camera:camera-core:${camerax_version}"
|
implementation "androidx.camera:camera-core:${camerax_version}"
|
||||||
implementation "androidx.camera:camera-camera2:${camerax_version}"
|
implementation "androidx.camera:camera-camera2:${camerax_version}"
|
||||||
// CameraX Lifecycle library
|
// CameraX Lifecycle library
|
||||||
implementation "androidx.camera:camera-lifecycle:$camerax_version"
|
implementation "androidx.camera:camera-lifecycle:$camerax_version"
|
||||||
|
|
||||||
// CameraX View class
|
// CameraX View class
|
||||||
implementation 'androidx.camera:camera-view:1.0.0-alpha14'
|
implementation 'androidx.camera:camera-view:1.0.0-alpha15'
|
||||||
|
|
||||||
implementation 'com.karumi:dexter:6.2.1'
|
implementation 'com.karumi:dexter:6.2.1'
|
||||||
|
|
||||||
|
@ -162,7 +162,7 @@ tasks.withType(Test) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
task jacocoTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest', 'createDebugCoverageReport']) {
|
task jacocoTestReport(type: JacocoReport, dependsOn: ['connectedDebugAndroidTest', 'testDebugUnitTest', 'createDebugCoverageReport']) {
|
||||||
|
|
||||||
reports {
|
reports {
|
||||||
xml.enabled = true
|
xml.enabled = true
|
||||||
|
|
|
@ -2,6 +2,7 @@ package com.h.pixeldroid
|
||||||
|
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.service.autofill.Validators.and
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.test.core.app.ActivityScenario
|
import androidx.test.core.app.ActivityScenario
|
||||||
import androidx.test.core.app.ApplicationProvider
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
@ -17,6 +18,7 @@ import com.h.pixeldroid.db.AppDatabase
|
||||||
import com.h.pixeldroid.db.InstanceDatabaseEntity
|
import com.h.pixeldroid.db.InstanceDatabaseEntity
|
||||||
import com.h.pixeldroid.db.UserDatabaseEntity
|
import com.h.pixeldroid.db.UserDatabaseEntity
|
||||||
import com.h.pixeldroid.fragments.feeds.postFeeds.PostViewHolder
|
import com.h.pixeldroid.fragments.feeds.postFeeds.PostViewHolder
|
||||||
|
import com.h.pixeldroid.testUtility.CustomMatchers.Companion.atPosition
|
||||||
import com.h.pixeldroid.testUtility.CustomMatchers.Companion.clickChildViewWithId
|
import com.h.pixeldroid.testUtility.CustomMatchers.Companion.clickChildViewWithId
|
||||||
import com.h.pixeldroid.testUtility.CustomMatchers.Companion.first
|
import com.h.pixeldroid.testUtility.CustomMatchers.Companion.first
|
||||||
import com.h.pixeldroid.testUtility.CustomMatchers.Companion.getText
|
import com.h.pixeldroid.testUtility.CustomMatchers.Companion.getText
|
||||||
|
@ -26,6 +28,8 @@ import com.h.pixeldroid.testUtility.CustomMatchers.Companion.typeTextInViewWithI
|
||||||
import com.h.pixeldroid.testUtility.MockServer
|
import com.h.pixeldroid.testUtility.MockServer
|
||||||
import com.h.pixeldroid.testUtility.initDB
|
import com.h.pixeldroid.testUtility.initDB
|
||||||
import com.h.pixeldroid.utils.DBUtils
|
import com.h.pixeldroid.utils.DBUtils
|
||||||
|
import org.hamcrest.CoreMatchers.allOf
|
||||||
|
import org.hamcrest.CoreMatchers.not
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
@ -217,7 +221,8 @@ class HomeFeedTest {
|
||||||
(1, clickChildViewWithId(R.id.sensitiveWarning)))
|
(1, clickChildViewWithId(R.id.sensitiveWarning)))
|
||||||
Thread.sleep(1000)
|
Thread.sleep(1000)
|
||||||
|
|
||||||
onView(second(withId(R.id.sensitiveWarning))).check(matches(withEffectiveVisibility(Visibility.GONE)))
|
onView(withId(R.id.list))
|
||||||
|
.check(matches(atPosition(1, not(withId(R.id.sensitiveWarning)))))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -3,14 +3,18 @@ package com.h.pixeldroid.testUtility
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.test.espresso.Espresso
|
import androidx.test.espresso.Espresso
|
||||||
import androidx.test.espresso.UiController
|
import androidx.test.espresso.UiController
|
||||||
import androidx.test.espresso.ViewAction
|
import androidx.test.espresso.ViewAction
|
||||||
import androidx.test.espresso.action.*
|
import androidx.test.espresso.action.*
|
||||||
|
import androidx.test.espresso.matcher.BoundedMatcher
|
||||||
import androidx.test.espresso.matcher.ViewMatchers
|
import androidx.test.espresso.matcher.ViewMatchers
|
||||||
import org.hamcrest.BaseMatcher
|
import org.hamcrest.BaseMatcher
|
||||||
|
import org.hamcrest.Description
|
||||||
import org.hamcrest.Matcher
|
import org.hamcrest.Matcher
|
||||||
|
|
||||||
|
|
||||||
abstract class CustomMatchers {
|
abstract class CustomMatchers {
|
||||||
companion object {
|
companion object {
|
||||||
fun <T> first(matcher: Matcher<T>): Matcher<T>? {
|
fun <T> first(matcher: Matcher<T>): Matcher<T>? {
|
||||||
|
@ -50,6 +54,24 @@ abstract class CustomMatchers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun atPosition(position: Int, itemMatcher: Matcher<View?>): Matcher<View?>? {
|
||||||
|
return object : BoundedMatcher<View?, RecyclerView>(RecyclerView::class.java) {
|
||||||
|
override fun describeTo(description: Description) {
|
||||||
|
description.appendText("has item at position $position: ")
|
||||||
|
itemMatcher.describeTo(description)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun matchesSafely(view: RecyclerView): Boolean {
|
||||||
|
val viewHolder = view.findViewHolderForAdapterPosition(position)
|
||||||
|
?: // has no item on such position
|
||||||
|
return false
|
||||||
|
return itemMatcher.matches(viewHolder.itemView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param percent can be 1 or 0
|
* @param percent can be 1 or 0
|
||||||
* 1: swipes all the way up
|
* 1: swipes all the way up
|
||||||
|
@ -60,8 +82,9 @@ abstract class CustomMatchers {
|
||||||
GeneralSwipeAction(
|
GeneralSwipeAction(
|
||||||
Swipe.SLOW,
|
Swipe.SLOW,
|
||||||
GeneralLocation.BOTTOM_CENTER,
|
GeneralLocation.BOTTOM_CENTER,
|
||||||
if(percent) GeneralLocation.TOP_CENTER else GeneralLocation.CENTER,
|
if (percent) GeneralLocation.TOP_CENTER else GeneralLocation.CENTER,
|
||||||
Press.FINGER)
|
Press.FINGER
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,8 +98,9 @@ abstract class CustomMatchers {
|
||||||
GeneralSwipeAction(
|
GeneralSwipeAction(
|
||||||
Swipe.SLOW,
|
Swipe.SLOW,
|
||||||
GeneralLocation.CENTER_RIGHT,
|
GeneralLocation.CENTER_RIGHT,
|
||||||
if(percent) GeneralLocation.CENTER_LEFT else GeneralLocation.CENTER,
|
if (percent) GeneralLocation.CENTER_LEFT else GeneralLocation.CENTER,
|
||||||
Press.FINGER)
|
Press.FINGER
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
package com.h.pixeldroid
|
package com.h.pixeldroid
|
||||||
|
|
||||||
|
import android.app.AlertDialog
|
||||||
import android.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
@ -13,21 +15,38 @@ import androidx.browser.customtabs.CustomTabsIntent
|
||||||
import com.h.pixeldroid.api.PixelfedAPI
|
import com.h.pixeldroid.api.PixelfedAPI
|
||||||
import com.h.pixeldroid.db.AppDatabase
|
import com.h.pixeldroid.db.AppDatabase
|
||||||
import com.h.pixeldroid.di.PixelfedAPIHolder
|
import com.h.pixeldroid.di.PixelfedAPIHolder
|
||||||
import com.h.pixeldroid.objects.Account
|
import com.h.pixeldroid.objects.*
|
||||||
import com.h.pixeldroid.objects.Application
|
|
||||||
import com.h.pixeldroid.objects.Instance
|
|
||||||
import com.h.pixeldroid.objects.Token
|
|
||||||
import com.h.pixeldroid.utils.DBUtils
|
import com.h.pixeldroid.utils.DBUtils
|
||||||
import com.h.pixeldroid.utils.DBUtils.Companion.storeInstance
|
|
||||||
import com.h.pixeldroid.utils.Utils
|
import com.h.pixeldroid.utils.Utils
|
||||||
import com.h.pixeldroid.utils.Utils.Companion.normalizeDomain
|
import com.h.pixeldroid.utils.Utils.Companion.normalizeDomain
|
||||||
|
import io.reactivex.Single
|
||||||
|
import io.reactivex.SingleObserver
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
|
import io.reactivex.disposables.Disposable
|
||||||
|
import io.reactivex.functions.BiFunction
|
||||||
|
import io.reactivex.functions.Function3
|
||||||
|
import io.reactivex.schedulers.Schedulers
|
||||||
import kotlinx.android.synthetic.main.activity_login.*
|
import kotlinx.android.synthetic.main.activity_login.*
|
||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
import retrofit2.Callback
|
import retrofit2.Callback
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
/**
|
||||||
|
Overview of the flow of the login process: (boxes are requests done in parallel,
|
||||||
|
since they do not depend on each other)
|
||||||
|
|
||||||
|
_________________________________
|
||||||
|
|[PixelfedAPI.registerApplication]|
|
||||||
|
|[PixelfedAPI.wellKnownNodeInfo] |
|
||||||
|
̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅ +----> [PixelfedAPI.nodeInfoSchema]
|
||||||
|
+----> [promptOAuth]
|
||||||
|
+---->____________________________
|
||||||
|
|[PixelfedAPI.instance] |
|
||||||
|
|[PixelfedAPI.obtainToken] |
|
||||||
|
̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅̅ +----> [PixelfedAPI.verifyCredentials]
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
class LoginActivity : AppCompatActivity() {
|
class LoginActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
@ -119,31 +138,85 @@ class LoginActivity : AppCompatActivity() {
|
||||||
hideKeyboard()
|
hideKeyboard()
|
||||||
loadingAnimation(true)
|
loadingAnimation(true)
|
||||||
|
|
||||||
apiHolder.setDomain(normalizedDomain).registerApplication(
|
pixelfedAPI = apiHolder.setDomain(normalizedDomain)
|
||||||
appName,"$oauthScheme://$PACKAGE_ID", SCOPE
|
|
||||||
).enqueue(object : Callback<Application> {
|
|
||||||
override fun onResponse(call: Call<Application>, response: Response<Application>) {
|
|
||||||
if (!response.isSuccessful) {
|
|
||||||
return failedRegistration()
|
|
||||||
}
|
|
||||||
preferences.edit()
|
|
||||||
.putString("domain", normalizedDomain)
|
|
||||||
.apply()
|
|
||||||
val credentials = response.body() as Application
|
|
||||||
val clientId = credentials.client_id ?: return failedRegistration()
|
|
||||||
preferences.edit()
|
|
||||||
.putString("clientID", clientId)
|
|
||||||
.putString("clientSecret", credentials.client_secret)
|
|
||||||
.apply()
|
|
||||||
promptOAuth(normalizedDomain, clientId)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFailure(call: Call<Application>, t: Throwable) {
|
Single.zip(
|
||||||
return failedRegistration()
|
pixelfedAPI.registerApplication(
|
||||||
|
appName,"$oauthScheme://$PACKAGE_ID", SCOPE
|
||||||
|
),
|
||||||
|
pixelfedAPI.wellKnownNodeInfo(),
|
||||||
|
BiFunction<Application, NodeInfoJRD, Pair<Application, NodeInfoJRD>> { application, nodeInfoJRD ->
|
||||||
|
// we get here when both results have come in:
|
||||||
|
Pair(application, nodeInfoJRD)
|
||||||
|
})
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(object : SingleObserver<Pair<Application, NodeInfoJRD>> {
|
||||||
|
override fun onSuccess(pair: Pair<Application, NodeInfoJRD>) {
|
||||||
|
val (credentials, nodeInfoJRD) = pair
|
||||||
|
val clientId = credentials.client_id ?: return failedRegistration()
|
||||||
|
preferences.edit()
|
||||||
|
.putString("domain", normalizedDomain)
|
||||||
|
.putString("clientID", clientId)
|
||||||
|
.putString("clientSecret", credentials.client_secret)
|
||||||
|
.apply()
|
||||||
|
|
||||||
|
//c.f. https://nodeinfo.diaspora.software/protocol.html for more info
|
||||||
|
val nodeInfoSchemaUrl = nodeInfoJRD.links.firstOrNull {
|
||||||
|
it.rel == "http://nodeinfo.diaspora.software/ns/schema/2.0"
|
||||||
|
}?.href ?: return failedRegistration(getString(R.string.instance_error))
|
||||||
|
|
||||||
|
nodeInfoSchema(normalizedDomain, clientId, nodeInfoSchemaUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(e: Throwable) {
|
||||||
|
//Error in any of the two requests will get to this
|
||||||
|
Log.e("registerAppToServer", e.message.toString())
|
||||||
|
failedRegistration()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSubscribe(d: Disposable) {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun nodeInfoSchema(
|
||||||
|
normalizedDomain: String,
|
||||||
|
clientId: String,
|
||||||
|
nodeInfoSchemaUrl: String
|
||||||
|
) {
|
||||||
|
pixelfedAPI.nodeInfoSchema(nodeInfoSchemaUrl).enqueue(object : Callback<NodeInfo> {
|
||||||
|
override fun onResponse(call: Call<NodeInfo>, response: Response<NodeInfo>) {
|
||||||
|
if (response.body() == null || !response.isSuccessful) {
|
||||||
|
return failedRegistration(getString(R.string.instance_error))
|
||||||
|
}
|
||||||
|
val nodeInfo = response.body() as NodeInfo
|
||||||
|
|
||||||
|
if (!nodeInfo.software?.name.orEmpty().contains("pixelfed")) {
|
||||||
|
val builder = AlertDialog.Builder(this@LoginActivity)
|
||||||
|
builder.apply {
|
||||||
|
setMessage(R.string.instance_not_pixelfed_warning)
|
||||||
|
setPositiveButton(R.string.instance_not_pixelfed_continue) { _, _ ->
|
||||||
|
promptOAuth(normalizedDomain, clientId)
|
||||||
|
}
|
||||||
|
setNegativeButton(R.string.instance_not_pixelfed_cancel) { _, _ ->
|
||||||
|
loadingAnimation(false)
|
||||||
|
wipeSharedSettings()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Create the AlertDialog
|
||||||
|
builder.show()
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
promptOAuth(normalizedDomain, clientId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override fun onFailure(call: Call<NodeInfo>, t: Throwable) {
|
||||||
|
failedRegistration(getString(R.string.instance_error))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun promptOAuth(normalizedDomain: String, client_id: String) {
|
private fun promptOAuth(normalizedDomain: String, client_id: String) {
|
||||||
|
|
||||||
val url = "$normalizedDomain/oauth/authorize?" +
|
val url = "$normalizedDomain/oauth/authorize?" +
|
||||||
|
@ -178,29 +251,45 @@ class LoginActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
//Successful authorization
|
//Successful authorization
|
||||||
val callback = object : Callback<Token> {
|
|
||||||
override fun onResponse(call: Call<Token>, response: Response<Token>) {
|
|
||||||
if (!response.isSuccessful || response.body() == null) {
|
|
||||||
return failedRegistration(getString(R.string.token_error))
|
|
||||||
}
|
|
||||||
authenticationSuccessful(response.body()!!.access_token)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFailure(call: Call<Token>, t: Throwable) {
|
|
||||||
return failedRegistration(getString(R.string.token_error))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pixelfedAPI = apiHolder.setDomain(domain)
|
pixelfedAPI = apiHolder.setDomain(domain)
|
||||||
pixelfedAPI.obtainToken(
|
|
||||||
clientId, clientSecret, "$oauthScheme://$PACKAGE_ID", SCOPE, code,
|
|
||||||
"authorization_code"
|
|
||||||
).enqueue(callback)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun authenticationSuccessful(accessToken: String) {
|
//TODO check why we can't do onErrorReturn { null } which would make more sense ¯\_(ツ)_/¯
|
||||||
saveUserAndInstance(accessToken)
|
//Also, maybe find a nicer way to do this, this feels hacky (although it can work fine)
|
||||||
wipeSharedSettings()
|
val nullInstance = Instance(null, null, null, null, null, null, null, null)
|
||||||
|
val nullToken = Token(null, null, null, null)
|
||||||
|
|
||||||
|
Single.zip(
|
||||||
|
pixelfedAPI.instance().onErrorReturn { nullInstance },
|
||||||
|
pixelfedAPI.obtainToken(
|
||||||
|
clientId, clientSecret, "$oauthScheme://$PACKAGE_ID", SCOPE, code,
|
||||||
|
"authorization_code"
|
||||||
|
).onErrorReturn { nullToken },
|
||||||
|
BiFunction<Instance, Token, Pair<Instance, Token>> { instance, token ->
|
||||||
|
// we get here when all results have come in:
|
||||||
|
Pair(instance, token)
|
||||||
|
})
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe(object : SingleObserver<Pair<Instance, Token>> {
|
||||||
|
override fun onSuccess(triple: Pair<Instance, Token>) {
|
||||||
|
val (instance, token) = triple
|
||||||
|
if(token == nullToken || token.access_token == null){
|
||||||
|
return failedRegistration(getString(R.string.token_error))
|
||||||
|
} else if(instance == nullInstance || instance.uri == null){
|
||||||
|
return failedRegistration(getString(R.string.instance_error))
|
||||||
|
}
|
||||||
|
|
||||||
|
DBUtils.storeInstance(db, instance)
|
||||||
|
storeUser(token.access_token, instance.uri)
|
||||||
|
wipeSharedSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onError(e: Throwable) {
|
||||||
|
Log.e("saveUserAndInstance", e.message.toString())
|
||||||
|
failedRegistration(getString(R.string.token_error))
|
||||||
|
}
|
||||||
|
override fun onSubscribe(d: Disposable) {}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun failedRegistration(message: String = getString(R.string.registration_failed)) {
|
private fun failedRegistration(message: String = getString(R.string.registration_failed)) {
|
||||||
|
@ -225,24 +314,6 @@ class LoginActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun saveUserAndInstance(accessToken: String) {
|
|
||||||
pixelfedAPI.instance().enqueue(object : Callback<Instance> {
|
|
||||||
override fun onFailure(call: Call<Instance>, t: Throwable) {
|
|
||||||
return failedRegistration(getString(R.string.instance_error))
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onResponse(call: Call<Instance>, response: Response<Instance>) {
|
|
||||||
if (response.isSuccessful && response.body() != null) {
|
|
||||||
val instance = response.body() as Instance
|
|
||||||
storeInstance(db, instance)
|
|
||||||
storeUser(accessToken, instance.uri)
|
|
||||||
} else {
|
|
||||||
return failedRegistration(getString(R.string.instance_error))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun storeUser(accessToken: String, instance: String) {
|
private fun storeUser(accessToken: String, instance: String) {
|
||||||
pixelfedAPI.verifyCredentials("Bearer $accessToken")
|
pixelfedAPI.verifyCredentials("Bearer $accessToken")
|
||||||
.enqueue(object : Callback<Account> {
|
.enqueue(object : Callback<Account> {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package com.h.pixeldroid.api
|
package com.h.pixeldroid.api
|
||||||
|
|
||||||
import com.h.pixeldroid.db.AppDatabase
|
|
||||||
import com.h.pixeldroid.objects.*
|
import com.h.pixeldroid.objects.*
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
|
import io.reactivex.Single
|
||||||
import okhttp3.MultipartBody
|
import okhttp3.MultipartBody
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
|
@ -10,9 +10,6 @@ import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
|
||||||
import retrofit2.converter.gson.GsonConverterFactory
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
import retrofit2.http.*
|
import retrofit2.http.*
|
||||||
import retrofit2.http.Field
|
import retrofit2.http.Field
|
||||||
import javax.inject.Inject
|
|
||||||
import javax.inject.Provider
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Implements the Pixelfed API
|
Implements the Pixelfed API
|
||||||
|
@ -23,7 +20,12 @@ import javax.inject.Provider
|
||||||
|
|
||||||
interface PixelfedAPI {
|
interface PixelfedAPI {
|
||||||
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@Deprecated(
|
||||||
|
"Use the DI-d PixelfedAPIHolder instead",
|
||||||
|
ReplaceWith("apiHolder.api")
|
||||||
|
)
|
||||||
fun create(baseUrl: String): PixelfedAPI {
|
fun create(baseUrl: String): PixelfedAPI {
|
||||||
return Retrofit.Builder()
|
return Retrofit.Builder()
|
||||||
.baseUrl(baseUrl)
|
.baseUrl(baseUrl)
|
||||||
|
@ -41,7 +43,8 @@ interface PixelfedAPI {
|
||||||
@Field("redirect_uris") redirect_uris: String,
|
@Field("redirect_uris") redirect_uris: String,
|
||||||
@Field("scopes") scopes: String? = null,
|
@Field("scopes") scopes: String? = null,
|
||||||
@Field("website") website: String? = null
|
@Field("website") website: String? = null
|
||||||
): Call<Application>
|
): Single<Application>
|
||||||
|
|
||||||
|
|
||||||
@FormUrlEncoded
|
@FormUrlEncoded
|
||||||
@POST("/oauth/token")
|
@POST("/oauth/token")
|
||||||
|
@ -52,7 +55,25 @@ interface PixelfedAPI {
|
||||||
@Field("scope") scope: String? = "read",
|
@Field("scope") scope: String? = "read",
|
||||||
@Field("code") code: String? = null,
|
@Field("code") code: String? = null,
|
||||||
@Field("grant_type") grant_type: String? = null
|
@Field("grant_type") grant_type: String? = null
|
||||||
): Call<Token>
|
): Single<Token>
|
||||||
|
|
||||||
|
// get instance configuration
|
||||||
|
@GET("/api/v1/instance")
|
||||||
|
fun instance() : Single<Instance>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instance info from the Nodeinfo .well_known (https://nodeinfo.diaspora.software/protocol.html) endpoint
|
||||||
|
*/
|
||||||
|
@GET("/.well-known/nodeinfo")
|
||||||
|
fun wellKnownNodeInfo() : Single<NodeInfoJRD>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instance info from [NodeInfo] (https://nodeinfo.diaspora.software/schema.html) endpoint
|
||||||
|
*/
|
||||||
|
@GET
|
||||||
|
fun nodeInfoSchema(
|
||||||
|
@Url nodeInfo_schema_url: String
|
||||||
|
) : Call<NodeInfo>
|
||||||
|
|
||||||
@FormUrlEncoded
|
@FormUrlEncoded
|
||||||
@POST("/api/v1/accounts/{id}/follow")
|
@POST("/api/v1/accounts/{id}/follow")
|
||||||
|
@ -240,10 +261,6 @@ interface PixelfedAPI {
|
||||||
@Part file: MultipartBody.Part
|
@Part file: MultipartBody.Part
|
||||||
): Observable<Attachment>
|
): Observable<Attachment>
|
||||||
|
|
||||||
// get instance configuration
|
|
||||||
@GET("/api/v1/instance")
|
|
||||||
fun instance() : Call<Instance>
|
|
||||||
|
|
||||||
// get discover
|
// get discover
|
||||||
@GET("/api/v2/discover/posts")
|
@GET("/api/v2/discover/posts")
|
||||||
fun discover(
|
fun discover(
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
package com.h.pixeldroid.objects
|
package com.h.pixeldroid.objects
|
||||||
|
|
||||||
data class Instance (
|
data class Instance (
|
||||||
val description: String,
|
val description: String?,
|
||||||
val email: String,
|
val email: String?,
|
||||||
val max_toot_chars: String = DEFAULT_MAX_TOOT_CHARS.toString(),
|
val max_toot_chars: String? = DEFAULT_MAX_TOOT_CHARS.toString(),
|
||||||
val registrations: Boolean,
|
val registrations: Boolean?,
|
||||||
val thumbnail: String,
|
val thumbnail: String?,
|
||||||
val title: String,
|
val title: String?,
|
||||||
val uri: String,
|
val uri: String?,
|
||||||
val version: String
|
val version: String?
|
||||||
) {
|
) {
|
||||||
companion object {
|
companion object {
|
||||||
const val DEFAULT_MAX_TOOT_CHARS = 500
|
const val DEFAULT_MAX_TOOT_CHARS = 500
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
package com.h.pixeldroid.objects
|
||||||
|
|
||||||
|
/*
|
||||||
|
See https://nodeinfo.diaspora.software/schema.html and https://pixelfed.social/api/nodeinfo/2.0.json
|
||||||
|
A lot of attributes we don't need are omitted, if in the future they are needed we
|
||||||
|
can make new data classes for them.
|
||||||
|
*/
|
||||||
|
|
||||||
|
data class NodeInfo (
|
||||||
|
val version: String?,
|
||||||
|
val software: Software?,
|
||||||
|
val protocols: List<String>?,
|
||||||
|
val openRegistrations: Boolean?,
|
||||||
|
val metadata: PixelfedMetadata?
|
||||||
|
){
|
||||||
|
data class Software(
|
||||||
|
val name: String?,
|
||||||
|
val version: String?
|
||||||
|
)
|
||||||
|
data class PixelfedMetadata(
|
||||||
|
val nodeName: String?,
|
||||||
|
val software: Software?,
|
||||||
|
val config: PixelfedConfig
|
||||||
|
){
|
||||||
|
data class Software(
|
||||||
|
val homepage: String?,
|
||||||
|
val repo: String?
|
||||||
|
)
|
||||||
|
}
|
||||||
|
data class PixelfedConfig(
|
||||||
|
val open_registration: Boolean?,
|
||||||
|
val uploader: Uploader?,
|
||||||
|
val activitypub: ActivityPub?,
|
||||||
|
val features: Features?
|
||||||
|
){
|
||||||
|
data class Uploader(
|
||||||
|
val max_photo_size: String?,
|
||||||
|
val max_caption_length: String?,
|
||||||
|
val album_limit: String?,
|
||||||
|
val image_quality: String?,
|
||||||
|
val optimize_image: Boolean?,
|
||||||
|
val optimize_video: Boolean?,
|
||||||
|
val media_types: String?,
|
||||||
|
val enforce_account_limit: Boolean?
|
||||||
|
)
|
||||||
|
|
||||||
|
data class ActivityPub(
|
||||||
|
val enabled: Boolean?,
|
||||||
|
val remote_follow: Boolean?
|
||||||
|
)
|
||||||
|
|
||||||
|
data class Features(
|
||||||
|
val mobile_apis: Boolean?,
|
||||||
|
val circles: Boolean?,
|
||||||
|
val stories: Boolean?,
|
||||||
|
val video: Boolean?
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class NodeInfoJRD(
|
||||||
|
val links: List<Link>
|
||||||
|
){
|
||||||
|
data class Link(
|
||||||
|
val rel: String?,
|
||||||
|
val href: String?
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
package com.h.pixeldroid.objects
|
package com.h.pixeldroid.objects
|
||||||
|
|
||||||
data class Token(
|
data class Token(
|
||||||
val access_token: String,
|
val access_token: String?,
|
||||||
val token_type: String,
|
val token_type: String?,
|
||||||
val scope: String,
|
val scope: String?,
|
||||||
val created_at: Int
|
val created_at: Int?
|
||||||
)
|
)
|
|
@ -37,13 +37,13 @@ class DBUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
fun storeInstance(db: AppDatabase, instance: Instance) {
|
fun storeInstance(db: AppDatabase, instance: Instance) {
|
||||||
val maxTootChars = instance.max_toot_chars.toInt()
|
val maxTootChars = instance.max_toot_chars?.toInt() ?: Instance.DEFAULT_MAX_TOOT_CHARS
|
||||||
val dbInstance = InstanceDatabaseEntity(
|
val dbInstance = InstanceDatabaseEntity(
|
||||||
//make sure not to normalize to https when localhost, to allow testing
|
//make sure not to normalize to https when localhost, to allow testing
|
||||||
uri = normalizeOrNot(instance.uri),
|
uri = normalizeOrNot(instance.uri.orEmpty()),
|
||||||
title = instance.title,
|
title = instance.title.orEmpty(),
|
||||||
max_toot_chars = maxTootChars,
|
max_toot_chars = maxTootChars,
|
||||||
thumbnail = instance.thumbnail
|
thumbnail = instance.thumbnail.orEmpty()
|
||||||
)
|
)
|
||||||
db.instanceDao().insertInstance(dbInstance)
|
db.instanceDao().insertInstance(dbInstance)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,9 @@
|
||||||
<string name="auth_failed">"Could not authenticate"</string>
|
<string name="auth_failed">"Could not authenticate"</string>
|
||||||
<string name="token_error">"Error getting token"</string>
|
<string name="token_error">"Error getting token"</string>
|
||||||
<string name="instance_error">"Could not get instance information"</string>
|
<string name="instance_error">"Could not get instance information"</string>
|
||||||
|
<string name="instance_not_pixelfed_warning">"This doesn't seem to be a Pixelfed instance, so the app could break in unexpected ways."</string>
|
||||||
|
<string name="instance_not_pixelfed_continue">"OK, continue anyway"</string>
|
||||||
|
<string name="instance_not_pixelfed_cancel">"Cancel logging in"</string>
|
||||||
<string name="title_activity_settings2">Settings</string>
|
<string name="title_activity_settings2">Settings</string>
|
||||||
<!-- Theme Preferences -->
|
<!-- Theme Preferences -->
|
||||||
<string name="theme_title">Application Theme</string>
|
<string name="theme_title">Application Theme</string>
|
||||||
|
|
|
@ -4,6 +4,8 @@ import com.github.tomakehurst.wiremock.client.WireMock.*
|
||||||
import com.github.tomakehurst.wiremock.junit.WireMockRule
|
import com.github.tomakehurst.wiremock.junit.WireMockRule
|
||||||
import com.h.pixeldroid.api.PixelfedAPI
|
import com.h.pixeldroid.api.PixelfedAPI
|
||||||
import com.h.pixeldroid.objects.*
|
import com.h.pixeldroid.objects.*
|
||||||
|
import io.reactivex.Single
|
||||||
|
import okhttp3.internal.wait
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
@ -106,9 +108,10 @@ class APIUnitTest {
|
||||||
.withHeader("Content-Type", "application/json")
|
.withHeader("Content-Type", "application/json")
|
||||||
.withBody(""" {"id":3197,"name":"Pixeldroid","website":null,"redirect_uri":"urn:ietf:wg:oauth:2.0:oob","client_id":3197,"client_secret":"hhRwLupqUJPghKsZzpZtxNV67g5DBdPYCqW6XE3m","vapid_key":null}"""
|
.withBody(""" {"id":3197,"name":"Pixeldroid","website":null,"redirect_uri":"urn:ietf:wg:oauth:2.0:oob","client_id":3197,"client_secret":"hhRwLupqUJPghKsZzpZtxNV67g5DBdPYCqW6XE3m","vapid_key":null}"""
|
||||||
)))
|
)))
|
||||||
val call: Call<Application> = PixelfedAPI.create("http://localhost:8089")
|
val call: Single<Application> = PixelfedAPI.create("http://localhost:8089")
|
||||||
.registerApplication("Pixeldroid", "urn:ietf:wg:oauth:2.0:oob", "read write follow")
|
.registerApplication("Pixeldroid", "urn:ietf:wg:oauth:2.0:oob", "read write follow")
|
||||||
val application: Application = call.execute().body()!!
|
|
||||||
|
val application: Application = call.toFuture().get()
|
||||||
assertEquals("3197", application.client_id)
|
assertEquals("3197", application.client_id)
|
||||||
assertEquals("hhRwLupqUJPghKsZzpZtxNV67g5DBdPYCqW6XE3m", application.client_secret)
|
assertEquals("hhRwLupqUJPghKsZzpZtxNV67g5DBdPYCqW6XE3m", application.client_secret)
|
||||||
assertEquals("Pixeldroid", application.name)
|
assertEquals("Pixeldroid", application.name)
|
||||||
|
@ -133,10 +136,10 @@ class APIUnitTest {
|
||||||
val OAUTH_SCHEME = "oauth2redirect"
|
val OAUTH_SCHEME = "oauth2redirect"
|
||||||
val SCOPE = "read write follow"
|
val SCOPE = "read write follow"
|
||||||
val PACKAGE_ID = "com.h.pixeldroid"
|
val PACKAGE_ID = "com.h.pixeldroid"
|
||||||
val call: Call<Token> = PixelfedAPI.create("http://localhost:8089")
|
val call: Single<Token> = PixelfedAPI.create("http://localhost:8089")
|
||||||
.obtainToken("123", "ssqdfqsdfqds", "$OAUTH_SCHEME://$PACKAGE_ID", SCOPE, "abc",
|
.obtainToken("123", "ssqdfqsdfqds", "$OAUTH_SCHEME://$PACKAGE_ID", SCOPE, "abc",
|
||||||
"authorization_code")
|
"authorization_code")
|
||||||
val token: Token = call.execute().body()!!
|
val token: Token = call.toFuture().get()
|
||||||
assertEquals("ZA-Yj3aBD8U8Cm7lKUp-lm9O9BmDgdhHzDeqsY8tlL0", token.access_token)
|
assertEquals("ZA-Yj3aBD8U8Cm7lKUp-lm9O9BmDgdhHzDeqsY8tlL0", token.access_token)
|
||||||
assertEquals("Bearer", token.token_type)
|
assertEquals("Bearer", token.token_type)
|
||||||
assertEquals("read write follow push", token.scope)
|
assertEquals("read write follow push", token.scope)
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.3.72'
|
ext.kotlin_version = '1.4.0'
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
jcenter()
|
||||||
|
|
Binary file not shown.
|
@ -1,6 +1,6 @@
|
||||||
#Sun Jul 26 16:18:03 CEST 2020
|
#Fri Aug 21 12:53:39 CEST 2020
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-all.zip
|
||||||
|
|
|
@ -1,5 +1,21 @@
|
||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright 2015 the original author or authors.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
#
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
##
|
##
|
||||||
## Gradle start up script for UN*X
|
## Gradle start up script for UN*X
|
||||||
|
@ -10,38 +26,38 @@
|
||||||
# Resolve links: $0 may be a link
|
# Resolve links: $0 may be a link
|
||||||
PRG="$0"
|
PRG="$0"
|
||||||
# Need this for relative symlinks.
|
# Need this for relative symlinks.
|
||||||
while [ -h "$PRG" ]; do
|
while [ -h "$PRG" ] ; do
|
||||||
ls=$(ls -ld "$PRG")
|
ls=`ls -ld "$PRG"`
|
||||||
link=$(expr "$ls" : '.*-> \(.*\)$')
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
if expr "$link" : '/.*' >/dev/null; then
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
PRG="$link"
|
PRG="$link"
|
||||||
else
|
else
|
||||||
PRG=$(dirname "$PRG")"/$link"
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
SAVED="$(pwd)"
|
SAVED="`pwd`"
|
||||||
cd "$(dirname \"$PRG\")/" >/dev/null
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
APP_HOME="$(pwd -P)"
|
APP_HOME="`pwd -P`"
|
||||||
cd "$SAVED" >/dev/null
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
APP_NAME="Gradle"
|
APP_NAME="Gradle"
|
||||||
APP_BASE_NAME=$(basename "$0")
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
DEFAULT_JVM_OPTS=""
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD="maximum"
|
MAX_FD="maximum"
|
||||||
|
|
||||||
warn() {
|
warn () {
|
||||||
echo "$*"
|
echo "$*"
|
||||||
}
|
}
|
||||||
|
|
||||||
die() {
|
die () {
|
||||||
echo
|
echo
|
||||||
echo "$*"
|
echo "$*"
|
||||||
echo
|
echo
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# OS specific support (must be 'true' or 'false').
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
@ -49,124 +65,121 @@ cygwin=false
|
||||||
msys=false
|
msys=false
|
||||||
darwin=false
|
darwin=false
|
||||||
nonstop=false
|
nonstop=false
|
||||||
case "$(uname)" in
|
case "`uname`" in
|
||||||
CYGWIN*)
|
CYGWIN* )
|
||||||
cygwin=true
|
cygwin=true
|
||||||
;;
|
;;
|
||||||
Darwin*)
|
Darwin* )
|
||||||
darwin=true
|
darwin=true
|
||||||
;;
|
;;
|
||||||
MINGW*)
|
MINGW* )
|
||||||
msys=true
|
msys=true
|
||||||
;;
|
;;
|
||||||
NONSTOP*)
|
NONSTOP* )
|
||||||
nonstop=true
|
nonstop=true
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
# Determine the Java command to use to start the JVM.
|
# Determine the Java command to use to start the JVM.
|
||||||
if [ -n "$JAVA_HOME" ]; then
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
if [ -x "$JAVA_HOME/jre/sh/java" ]; then
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
# IBM's JDK on AIX uses strange locations for the executables
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
else
|
else
|
||||||
JAVACMD="$JAVA_HOME/bin/java"
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
fi
|
fi
|
||||||
if [ ! -x "$JAVACMD" ]; then
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
JAVACMD="java"
|
JAVACMD="java"
|
||||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ]; then
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
MAX_FD_LIMIT=$(ulimit -H -n)
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ] ; then
|
||||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ]; then
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
MAX_FD="$MAX_FD_LIMIT"
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
fi
|
fi
|
||||||
ulimit -n $MAX_FD
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# For Darwin, add options to specify how the application appears in the dock
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
if $darwin; then
|
if $darwin; then
|
||||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# For Cygwin, switch paths to Windows format before running java
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
if $cygwin; then
|
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||||
APP_HOME=$(cygpath --path --mixed "$APP_HOME")
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
CLASSPATH=$(cygpath --path --mixed "$CLASSPATH")
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
JAVACMD=$(cygpath --unix "$JAVACMD")
|
|
||||||
|
|
||||||
# We build the pattern for arguments to be converted via cygpath
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
ROOTDIRSRAW=$(find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null)
|
|
||||||
SEP=""
|
|
||||||
for dir in $ROOTDIRSRAW; do
|
|
||||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
|
||||||
SEP="|"
|
|
||||||
done
|
|
||||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
|
||||||
# Add a user-defined pattern to the cygpath arguments
|
|
||||||
if [ "$GRADLE_CYGPATTERN" != "" ]; then
|
|
||||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
|
||||||
fi
|
|
||||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
|
||||||
i=0
|
|
||||||
for arg in "$@"; do
|
|
||||||
CHECK=$(echo "$arg" | egrep -c "$OURCYGPATTERN" -)
|
|
||||||
CHECK2=$(echo "$arg" | egrep -c "^-") ### Determine if an option
|
|
||||||
|
|
||||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ]; then ### Added a condition
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
eval $(echo args$i)=$(cygpath --path --ignore --mixed "$arg")
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
else
|
SEP=""
|
||||||
eval $(echo args$i)="\"$arg\""
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
fi
|
fi
|
||||||
i=$((i + 1))
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
done
|
i=0
|
||||||
case $i in
|
for arg in "$@" ; do
|
||||||
0) set -- ;;
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
1) set -- "$args0" ;;
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
2) set -- "$args0" "$args1" ;;
|
|
||||||
3) set -- "$args0" "$args1" "$args2" ;;
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
else
|
||||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
eval `echo args$i`="\"$arg\""
|
||||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
fi
|
||||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
i=`expr $i + 1`
|
||||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
done
|
||||||
esac
|
case $i in
|
||||||
|
0) set -- ;;
|
||||||
|
1) set -- "$args0" ;;
|
||||||
|
2) set -- "$args0" "$args1" ;;
|
||||||
|
3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Escape application args
|
# Escape application args
|
||||||
save() {
|
save () {
|
||||||
for i; do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/"; done
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
echo " "
|
echo " "
|
||||||
}
|
}
|
||||||
APP_ARGS=$(save "$@")
|
APP_ARGS=`save "$@"`
|
||||||
|
|
||||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
|
||||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
|
||||||
cd "$(dirname "$0")"
|
|
||||||
fi
|
|
||||||
|
|
||||||
exec "$JAVACMD" "$@"
|
exec "$JAVACMD" "$@"
|
||||||
|
|
|
@ -1,3 +1,19 @@
|
||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%" == "" @echo off
|
@if "%DEBUG%" == "" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
|
@ -13,15 +29,18 @@ if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
set APP_BASE_NAME=%~n0
|
set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
set DEFAULT_JVM_OPTS=
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
@rem Find java.exe
|
@rem Find java.exe
|
||||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
set JAVA_EXE=java.exe
|
set JAVA_EXE=java.exe
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if "%ERRORLEVEL%" == "0" goto init
|
if "%ERRORLEVEL%" == "0" goto execute
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
@ -35,7 +54,7 @@ goto fail
|
||||||
set JAVA_HOME=%JAVA_HOME:"=%
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
if exist "%JAVA_EXE%" goto init
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
@ -45,28 +64,14 @@ echo location of your Java installation.
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
:init
|
|
||||||
@rem Get command-line arguments, handling Windows variants
|
|
||||||
|
|
||||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
|
||||||
|
|
||||||
:win9xME_args
|
|
||||||
@rem Slurp the command line arguments.
|
|
||||||
set CMD_LINE_ARGS=
|
|
||||||
set _SKIP=2
|
|
||||||
|
|
||||||
:win9xME_args_slurp
|
|
||||||
if "x%~1" == "x" goto execute
|
|
||||||
|
|
||||||
set CMD_LINE_ARGS=%*
|
|
||||||
|
|
||||||
:execute
|
:execute
|
||||||
@rem Setup the command line
|
@rem Setup the command line
|
||||||
|
|
||||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
@rem Execute Gradle
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
:end
|
:end
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
|
Loading…
Reference in New Issue