parent
b901b68383
commit
f2cb92dd2b
@ -554,9 +554,6 @@
|
|||||||
<string name="preference_shortcut_badges_summary">Show a badge which indicates to which app a shortcut belongs</string>
|
<string name="preference_shortcut_badges_summary">Show a badge which indicates to which app a shortcut belongs</string>
|
||||||
<string name="preference_plugin_badges">Plugin badges</string>
|
<string name="preference_plugin_badges">Plugin badges</string>
|
||||||
<string name="preference_plugin_badges_summary">Indicate by which plugin a search result was created</string>
|
<string name="preference_plugin_badges_summary">Indicate by which plugin a search result was created</string>
|
||||||
<string name="preference_microsoft">Microsoft</string>
|
|
||||||
<string name="preference_ms_signin">Sign in with Microsoft</string>
|
|
||||||
<string name="preference_ms_signin_summary">Sign in to search OneDrive</string>
|
|
||||||
<string name="preference_nextcloud_signin">Sign in to Nextcloud</string>
|
<string name="preference_nextcloud_signin">Sign in to Nextcloud</string>
|
||||||
<string name="preference_nextcloud_signin_summary">Sign in to search your Nextcloud server</string>
|
<string name="preference_nextcloud_signin_summary">Sign in to search your Nextcloud server</string>
|
||||||
<string name="preference_nextcloud">Nextcloud</string>
|
<string name="preference_nextcloud">Nextcloud</string>
|
||||||
@ -697,8 +694,6 @@
|
|||||||
<string name="no_account_nextcloud">You haven\'t connected a Nextcloud account yet</string>
|
<string name="no_account_nextcloud">You haven\'t connected a Nextcloud account yet</string>
|
||||||
<!-- Used in an info banner if a specific feature requires an Owncloud account -->
|
<!-- Used in an info banner if a specific feature requires an Owncloud account -->
|
||||||
<string name="no_account_owncloud">You haven\'t connected an Owncloud account yet</string>
|
<string name="no_account_owncloud">You haven\'t connected an Owncloud account yet</string>
|
||||||
<!-- Used in an info banner if a specific feature requires a Microsoft account -->
|
|
||||||
<string name="no_account_microsoft">You haven\'t connected a Microsoft account yet</string>
|
|
||||||
<!-- Used in an info banner if a specific feature requires a Google account -->
|
<!-- Used in an info banner if a specific feature requires a Google account -->
|
||||||
<string name="no_account_google">You haven\'t connected a Google account yet</string>
|
<string name="no_account_google">You haven\'t connected a Google account yet</string>
|
||||||
<!-- Used in info banners that indicate that an account is required to enable a certain feature -->
|
<!-- Used in info banners that indicate that an account is required to enable a certain feature -->
|
||||||
|
|||||||
@ -117,12 +117,6 @@ google-apiclient = { group = "com.google.api-client", name = "google-api-client"
|
|||||||
google-drive = { group = "com.google.apis", name = "google-api-services-drive", version = "v3-rev20221219-2.0.0" }
|
google-drive = { group = "com.google.apis", name = "google-api-services-drive", version = "v3-rev20221219-2.0.0" }
|
||||||
google-oauth2 = { group = "com.google.apis", name = "google-api-services-oauth2", version = "v2-rev20200213-2.0.0" }
|
google-oauth2 = { group = "com.google.apis", name = "google-api-services-oauth2", version = "v2-rev20200213-2.0.0" }
|
||||||
|
|
||||||
gson = { group = "com.google.code.gson", name = "gson", version = "2.10.1" }
|
|
||||||
guava = { group = "com.google.guava", name = "guava", version = "31.1-android" }
|
|
||||||
|
|
||||||
microsoft-graph = { group = "com.microsoft.graph", name = "microsoft-graph", version = "5.53.0" }
|
|
||||||
microsoft-identity = { group = "com.microsoft.identity.client", name = "msal", version = "4.2.0" }
|
|
||||||
|
|
||||||
protobuf-protoc = { group = "com.google.protobuf", name = "protoc", version.ref = "protobuf" }
|
protobuf-protoc = { group = "com.google.protobuf", name = "protoc", version.ref = "protobuf" }
|
||||||
protobuf-javalite = { group = "com.google.protobuf", name = "protobuf-javalite", version.ref = "protobuf" }
|
protobuf-javalite = { group = "com.google.protobuf", name = "protobuf-javalite", version.ref = "protobuf" }
|
||||||
|
|
||||||
|
|||||||
2
libs/ms-services/.gitignore
vendored
2
libs/ms-services/.gitignore
vendored
@ -1,2 +0,0 @@
|
|||||||
/build
|
|
||||||
*/**/msal_auth_config.json
|
|
||||||
@ -1,47 +0,0 @@
|
|||||||
plugins {
|
|
||||||
alias(libs.plugins.android.library)
|
|
||||||
alias(libs.plugins.kotlin.android)
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
|
||||||
compileSdk = libs.versions.compileSdk.get().toInt()
|
|
||||||
|
|
||||||
defaultConfig {
|
|
||||||
minSdk = libs.versions.minSdk.get().toInt()
|
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
|
||||||
consumerProguardFiles("consumer-rules.pro")
|
|
||||||
}
|
|
||||||
|
|
||||||
buildTypes {
|
|
||||||
release {
|
|
||||||
proguardFiles(
|
|
||||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
|
||||||
"proguard-rules.pro"
|
|
||||||
)
|
|
||||||
consumerProguardFile("proguard-rules.pro")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
compileOptions {
|
|
||||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
|
||||||
targetCompatibility = JavaVersion.VERSION_1_8
|
|
||||||
}
|
|
||||||
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = "1.8"
|
|
||||||
}
|
|
||||||
namespace = "de.mm20.launcher2.msservices"
|
|
||||||
}
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation(libs.bundles.kotlin)
|
|
||||||
implementation(libs.androidx.core)
|
|
||||||
implementation(libs.androidx.appcompat)
|
|
||||||
|
|
||||||
implementation(libs.microsoft.identity)
|
|
||||||
implementation(libs.microsoft.graph)
|
|
||||||
implementation(libs.guava)
|
|
||||||
|
|
||||||
implementation(project(":core:crashreporter"))
|
|
||||||
}
|
|
||||||
23
libs/ms-services/proguard-rules.pro
vendored
23
libs/ms-services/proguard-rules.pro
vendored
@ -1,23 +0,0 @@
|
|||||||
# Add project specific ProGuard rules here.
|
|
||||||
# You can control the set of applied configuration files using the
|
|
||||||
# proguardFiles setting in build.gradle.kts.kts.kts.
|
|
||||||
#
|
|
||||||
# For more details, see
|
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
|
||||||
|
|
||||||
# If your project uses WebView with JS, uncomment the following
|
|
||||||
# and specify the fully qualified class name to the JavaScript interface
|
|
||||||
# class:
|
|
||||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
|
||||||
# public *;
|
|
||||||
#}
|
|
||||||
|
|
||||||
# Uncomment this to preserve the line number information for
|
|
||||||
# debugging stack traces.
|
|
||||||
#-keepattributes SourceFile,LineNumberTable
|
|
||||||
|
|
||||||
# If you keep the line number information, uncomment this to
|
|
||||||
# hide the original source file name.
|
|
||||||
#-renamesourcefileattribute SourceFile
|
|
||||||
|
|
||||||
-keep class com.microsoft.graph.requests.** { *; }
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"client_id" : "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
|
||||||
"authorization_user_agent" : "DEFAULT",
|
|
||||||
"account_mode": "SINGLE",
|
|
||||||
"redirect_uri" : "msauth://de.mm20.launcher2.debug/xxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
|
||||||
"authorities" : [
|
|
||||||
{
|
|
||||||
"type": "AAD",
|
|
||||||
"audience": {
|
|
||||||
"type": "AzureADandPersonalMicrosoftAccount",
|
|
||||||
"tenant_id": "common"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
|
|
||||||
<application>
|
|
||||||
<activity
|
|
||||||
android:name="com.microsoft.identity.client.BrowserTabActivity"
|
|
||||||
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:host="${applicationId}"
|
|
||||||
android:scheme="msauth" />
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
|
||||||
</application>
|
|
||||||
</manifest>
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
package de.mm20.launcher2.msservices
|
|
||||||
|
|
||||||
import com.microsoft.graph.models.DriveItem as MSDriveItem
|
|
||||||
|
|
||||||
data class DriveItem(
|
|
||||||
val id : String,
|
|
||||||
val label : String,
|
|
||||||
val mimeType : String,
|
|
||||||
val size: Long,
|
|
||||||
val isDirectory : Boolean,
|
|
||||||
val webUrl: String,
|
|
||||||
val meta: DriveItemMeta
|
|
||||||
) {
|
|
||||||
companion object {
|
|
||||||
fun fromApiDriveItem(driveItem: MSDriveItem) : DriveItem? {
|
|
||||||
return DriveItem(
|
|
||||||
id = driveItem.id ?: return null,
|
|
||||||
label = driveItem.name ?: return null,
|
|
||||||
mimeType = driveItem.file?.mimeType ?: "inode/directory",
|
|
||||||
size = driveItem.size ?: 0,
|
|
||||||
isDirectory = driveItem.file == null,
|
|
||||||
webUrl = driveItem.webUrl ?: return null,
|
|
||||||
meta = DriveItemMeta(
|
|
||||||
owner = driveItem.shared?.owner?.user?.displayName,
|
|
||||||
createdBy = driveItem.createdBy?.user?.displayName,
|
|
||||||
width = driveItem.image?.width ?: driveItem.video?.width,
|
|
||||||
height = driveItem.image?.height ?: driveItem.video?.height
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class DriveItemMeta(
|
|
||||||
val owner: String?,
|
|
||||||
val createdBy: String?,
|
|
||||||
val width: Int?,
|
|
||||||
val height: Int?
|
|
||||||
)
|
|
||||||
@ -1,212 +0,0 @@
|
|||||||
package de.mm20.launcher2.msservices
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.content.Context
|
|
||||||
import android.util.Log
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.core.content.edit
|
|
||||||
import com.azure.core.credential.AccessToken
|
|
||||||
import com.azure.core.credential.TokenCredential
|
|
||||||
import com.microsoft.graph.authentication.TokenCredentialAuthProvider
|
|
||||||
import com.microsoft.graph.core.ClientException
|
|
||||||
import com.microsoft.graph.http.GraphServiceException
|
|
||||||
import com.microsoft.graph.models.DriveSearchParameterSet
|
|
||||||
import com.microsoft.graph.requests.GraphServiceClient
|
|
||||||
import com.microsoft.identity.client.AuthenticationCallback
|
|
||||||
import com.microsoft.identity.client.IAuthenticationResult
|
|
||||||
import com.microsoft.identity.client.ISingleAccountPublicClientApplication
|
|
||||||
import com.microsoft.identity.client.PublicClientApplication
|
|
||||||
import com.microsoft.identity.client.exception.MsalClientException
|
|
||||||
import com.microsoft.identity.client.exception.MsalException
|
|
||||||
import de.mm20.launcher2.crashreporter.CrashReporter
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import okhttp3.Request
|
|
||||||
import reactor.core.publisher.Mono
|
|
||||||
import java.net.URLEncoder
|
|
||||||
import kotlin.coroutines.resume
|
|
||||||
import kotlin.coroutines.suspendCoroutine
|
|
||||||
|
|
||||||
class MicrosoftGraphApiHelper(val context: Context) {
|
|
||||||
|
|
||||||
private var accessToken: String? = null
|
|
||||||
private val client: GraphServiceClient<Request> = GraphServiceClient
|
|
||||||
.builder()
|
|
||||||
.authenticationProvider(TokenCredentialAuthProvider {
|
|
||||||
Mono.just(AccessToken(accessToken, null))
|
|
||||||
})
|
|
||||||
.buildClient()
|
|
||||||
|
|
||||||
private var clientApplication: ISingleAccountPublicClientApplication? = null
|
|
||||||
|
|
||||||
private suspend fun getClientApplication(): ISingleAccountPublicClientApplication? {
|
|
||||||
val resId = getConfigResId()
|
|
||||||
if (resId == 0) return null
|
|
||||||
if (clientApplication == null) {
|
|
||||||
clientApplication = withContext(Dispatchers.IO) {
|
|
||||||
try {
|
|
||||||
PublicClientApplication.createSingleAccountPublicClientApplication(
|
|
||||||
context.applicationContext,
|
|
||||||
resId
|
|
||||||
)
|
|
||||||
} catch (e: MsalClientException) {
|
|
||||||
CrashReporter.logException(e)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return clientApplication
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun acquireAccessToken(): Boolean {
|
|
||||||
val result = withContext(Dispatchers.IO) {
|
|
||||||
try {
|
|
||||||
val application = getClientApplication() ?: return@withContext null
|
|
||||||
val authority = application.configuration.defaultAuthority.authorityURL.toString()
|
|
||||||
application.acquireTokenSilent(SCOPES, authority)
|
|
||||||
} catch (e: MsalException) {
|
|
||||||
CrashReporter.logException(e)
|
|
||||||
logout()
|
|
||||||
null
|
|
||||||
} catch (e: ClientException) {
|
|
||||||
CrashReporter.logException(e)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
accessToken = result?.accessToken
|
|
||||||
return result != null
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun login(context: Activity) {
|
|
||||||
val clientApplication = getClientApplication() ?: return run {
|
|
||||||
withContext(Dispatchers.Main) {
|
|
||||||
Toast.makeText(context, "Something went wrong, please check the logs", Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
suspendCoroutine<IAuthenticationResult?> {
|
|
||||||
clientApplication.signIn(context, "", SCOPES, object : AuthenticationCallback {
|
|
||||||
override fun onSuccess(authenticationResult: IAuthenticationResult?) {
|
|
||||||
accessToken = authenticationResult?.accessToken
|
|
||||||
it.resume(authenticationResult)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCancel() {
|
|
||||||
it.resume(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(exception: MsalException?) {
|
|
||||||
if (exception != null) Log.e("MM20", exception.stackTraceToString())
|
|
||||||
it.resume(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
|
||||||
}
|
|
||||||
loadAccountName()
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun logout() {
|
|
||||||
accessToken = null
|
|
||||||
context.getSharedPreferences(PREFS, Context.MODE_PRIVATE).edit {
|
|
||||||
putString(PREF_ACCOUNT_NAME, null)
|
|
||||||
}
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
try {
|
|
||||||
getClientApplication()?.signOut()
|
|
||||||
} catch (e: MsalClientException) {
|
|
||||||
CrashReporter.logException(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun getUser(): MsUser? {
|
|
||||||
val name = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE).getString(
|
|
||||||
PREF_ACCOUNT_NAME,
|
|
||||||
null
|
|
||||||
) ?: loadAccountName()
|
|
||||||
|
|
||||||
|
|
||||||
return name?.let {
|
|
||||||
MsUser(name = it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun loadAccountName(): String? {
|
|
||||||
if (!isLoggedIn()) return null
|
|
||||||
if (!acquireAccessToken()) return null
|
|
||||||
return withContext(Dispatchers.IO) {
|
|
||||||
try {
|
|
||||||
val user = client.me().buildRequest().get() ?: return@withContext null
|
|
||||||
val name = user.displayName ?: user.mail ?: "Microsoft User"
|
|
||||||
context.getSharedPreferences(PREFS, Context.MODE_PRIVATE).edit {
|
|
||||||
putString(PREF_ACCOUNT_NAME, name)
|
|
||||||
}
|
|
||||||
return@withContext name
|
|
||||||
} catch (e: GraphServiceException) {
|
|
||||||
CrashReporter.logException(e)
|
|
||||||
logout()
|
|
||||||
} catch (e: ClientException) {
|
|
||||||
CrashReporter.logException(e)
|
|
||||||
}
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun isLoggedIn(): Boolean {
|
|
||||||
return withContext(Dispatchers.IO) {
|
|
||||||
getClientApplication()?.currentAccount?.currentAccount != null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
suspend fun queryOneDriveFiles(query: String): List<DriveItem>? {
|
|
||||||
if (!acquireAccessToken()) return null
|
|
||||||
return try {
|
|
||||||
withContext(Dispatchers.IO) {
|
|
||||||
client.me().drive().search(
|
|
||||||
DriveSearchParameterSet.newBuilder()
|
|
||||||
.withQ(query)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.buildRequest()
|
|
||||||
.select("id,name,file,size,video,image,webUrl,shared,createdBy")
|
|
||||||
.top(10)
|
|
||||||
.get()
|
|
||||||
?.currentPage
|
|
||||||
?.mapNotNull { DriveItem.fromApiDriveItem(it) }
|
|
||||||
}
|
|
||||||
} catch (e: GraphServiceException) {
|
|
||||||
CrashReporter.logException(e)
|
|
||||||
null
|
|
||||||
} catch (e: ClientException) {
|
|
||||||
CrashReporter.logException(e)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun isAvailable(): Boolean {
|
|
||||||
return getConfigResId() != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getConfigResId(): Int {
|
|
||||||
return context.resources.getIdentifier("msal_auth_config", "raw", context.packageName)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private lateinit var instance: MicrosoftGraphApiHelper
|
|
||||||
|
|
||||||
fun getInstance(context: Context): MicrosoftGraphApiHelper {
|
|
||||||
if (!Companion::instance.isInitialized) instance =
|
|
||||||
MicrosoftGraphApiHelper(context.applicationContext)
|
|
||||||
return instance
|
|
||||||
}
|
|
||||||
|
|
||||||
private val SCOPES = arrayOf(
|
|
||||||
"User.Read",
|
|
||||||
"Files.Read.All"
|
|
||||||
)
|
|
||||||
|
|
||||||
const val PREFS = "ms-account"
|
|
||||||
const val PREF_ACCOUNT_NAME = "name"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
package de.mm20.launcher2.msservices
|
|
||||||
|
|
||||||
data class MsUser(
|
|
||||||
val name: String
|
|
||||||
)
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"client_id" : "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
|
||||||
"authorization_user_agent" : "DEFAULT",
|
|
||||||
"account_mode": "SINGLE",
|
|
||||||
"redirect_uri" : "msauth://de.mm20.launcher2.release/xxxxxxxxxxxxxxxxxx",
|
|
||||||
"authorities" : [
|
|
||||||
{
|
|
||||||
"type": "AAD",
|
|
||||||
"audience": {
|
|
||||||
"type": "AzureADandPersonalMicrosoftAccount",
|
|
||||||
"tenant_id": "common"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -93,13 +93,6 @@ internal class AccountsRepositoryImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun getMicrosoftAccount(): Account? {
|
|
||||||
return null
|
|
||||||
/*return msGraphApiHelper.getUser()?.let {
|
|
||||||
Account(it.name, AccountType.Microsoft)
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
|
|
||||||
private suspend fun getNextcloudAccount(): Account? {
|
private suspend fun getNextcloudAccount(): Account? {
|
||||||
return nextcloudApiHelper.getLoggedInUser()?.let {
|
return nextcloudApiHelper.getLoggedInUser()?.let {
|
||||||
Account(it.displayName, AccountType.Nextcloud)
|
Account(it.displayName, AccountType.Nextcloud)
|
||||||
|
|||||||
@ -10,7 +10,6 @@ dependencyResolutionManagement {
|
|||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven(url = "https://pkgs.dev.azure.com/MicrosoftDeviceSDK/DuoSDK-Public/_packaging/Duo-SDK-Feed/maven/v1")
|
|
||||||
jcenter() // For tinypinyin
|
jcenter() // For tinypinyin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -60,7 +59,6 @@ include(":libs:nextcloud")
|
|||||||
include(":libs:owncloud")
|
include(":libs:owncloud")
|
||||||
include(":libs:webdav")
|
include(":libs:webdav")
|
||||||
include(":libs:g-services")
|
include(":libs:g-services")
|
||||||
include(":libs:ms-services")
|
|
||||||
include(":services:global-actions")
|
include(":services:global-actions")
|
||||||
include(":services:widgets")
|
include(":services:widgets")
|
||||||
include(":services:favorites")
|
include(":services:favorites")
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user