diff options
Diffstat (limited to 'app/src/main/java/dev/msfjarvis/aps/util/services')
3 files changed, 0 insertions, 537 deletions
diff --git a/app/src/main/java/dev/msfjarvis/aps/util/services/ClipboardService.kt b/app/src/main/java/dev/msfjarvis/aps/util/services/ClipboardService.kt deleted file mode 100644 index 34c3a989..00000000 --- a/app/src/main/java/dev/msfjarvis/aps/util/services/ClipboardService.kt +++ /dev/null @@ -1,206 +0,0 @@ -/* - * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ -package dev.msfjarvis.aps.util.services - -import android.app.Notification -import android.app.NotificationChannel -import android.app.NotificationManager -import android.app.PendingIntent -import android.app.Service -import android.content.ClipData -import android.content.Intent -import android.os.Build -import android.os.IBinder -import androidx.annotation.RequiresApi -import androidx.core.app.NotificationCompat -import androidx.core.content.getSystemService -import dev.msfjarvis.aps.R -import dev.msfjarvis.aps.util.extensions.clipboard -import dev.msfjarvis.aps.util.extensions.sharedPrefs -import dev.msfjarvis.aps.util.settings.PreferenceKeys -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.cancel -import kotlinx.coroutines.delay -import kotlinx.coroutines.isActive -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import logcat.logcat - -class ClipboardService : Service() { - - private val scope = CoroutineScope(Job() + Dispatchers.Main) - - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - if (intent != null) { - when (intent.action) { - ACTION_CLEAR -> { - clearClipboard() - stopForeground(true) - stopSelf() - return super.onStartCommand(intent, flags, startId) - } - ACTION_START -> { - val time = intent.getIntExtra(EXTRA_NOTIFICATION_TIME, 45) - - if (time == 0) { - stopSelf() - } - - createNotification(time) - scope.launch { - withContext(Dispatchers.IO) { startTimer(time) } - withContext(Dispatchers.Main) { - clearClipboard() - stopForeground(true) - stopSelf() - } - } - return START_NOT_STICKY - } - } - } - - return super.onStartCommand(intent, flags, startId) - } - - override fun onBind(intent: Intent?): IBinder? { - return null - } - - override fun onDestroy() { - scope.cancel() - super.onDestroy() - } - - private fun clearClipboard() { - val deepClear = sharedPrefs.getBoolean(PreferenceKeys.CLEAR_CLIPBOARD_HISTORY, false) - val clipboard = clipboard - - if (clipboard != null) { - scope.launch { - logcat { "Clearing the clipboard" } - val clip = ClipData.newPlainText("pgp_handler_result_pm", "") - clipboard.setPrimaryClip(clip) - if (deepClear) { - withContext(Dispatchers.IO) { - repeat(CLIPBOARD_CLEAR_COUNT) { - val count = (it * 500).toString() - clipboard.setPrimaryClip(ClipData.newPlainText(count, count)) - } - } - } - } - } else { - logcat { "Cannot get clipboard manager service" } - } - } - - private suspend fun startTimer(showTime: Int) { - var current = 0 - while (scope.isActive && current < showTime) { - // Block for 1s or until cancel is signalled - current++ - delay(1000) - } - } - - private fun createNotification(clearTime: Int) { - val clearTimeMs = clearTime * 1000L - val clearIntent = Intent(this, ClipboardService::class.java).apply { action = ACTION_CLEAR } - val pendingIntent = - if (Build.VERSION.SDK_INT >= 26) { - PendingIntent.getForegroundService( - this, - 0, - clearIntent, - if (Build.VERSION.SDK_INT >= 31) { - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE - } else { - PendingIntent.FLAG_UPDATE_CURRENT - } - ) - } else { - PendingIntent.getService( - this, - 0, - clearIntent, - if (Build.VERSION.SDK_INT >= 31) { - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE - } else { - PendingIntent.FLAG_UPDATE_CURRENT - }, - ) - } - val notification = - if (Build.VERSION.SDK_INT <= 23) { - createNotificationApi23(pendingIntent) - } else { - createNotificationApi24(pendingIntent, clearTimeMs) - } - - createNotificationChannel() - startForeground(1, notification) - } - - private fun createNotificationApi23(pendingIntent: PendingIntent): Notification { - return NotificationCompat.Builder(this, CHANNEL_ID) - .setContentTitle(getString(R.string.app_name)) - .setContentText(getString(R.string.tap_clear_clipboard)) - .setSmallIcon(R.drawable.ic_action_secure_24dp) - .setContentIntent(pendingIntent) - .setUsesChronometer(true) - .setPriority(NotificationCompat.PRIORITY_LOW) - .build() - } - - @RequiresApi(24) - private fun createNotificationApi24( - pendingIntent: PendingIntent, - clearTimeMs: Long - ): Notification { - return NotificationCompat.Builder(this, CHANNEL_ID) - .setContentTitle(getString(R.string.app_name)) - .setContentText(getString(R.string.tap_clear_clipboard)) - .setSmallIcon(R.drawable.ic_action_secure_24dp) - .setContentIntent(pendingIntent) - .setUsesChronometer(true) - .setChronometerCountDown(true) - .setShowWhen(true) - .setWhen(System.currentTimeMillis() + clearTimeMs) - .setPriority(NotificationCompat.PRIORITY_LOW) - .build() - } - - private fun createNotificationChannel() { - if (Build.VERSION.SDK_INT >= 26) { - val serviceChannel = - NotificationChannel( - CHANNEL_ID, - getString(R.string.app_name), - NotificationManager.IMPORTANCE_LOW - ) - val manager = getSystemService<NotificationManager>() - if (manager != null) { - manager.createNotificationChannel(serviceChannel) - } else { - logcat { "Failed to create notification channel" } - } - } - } - - companion object { - - const val ACTION_START = "ACTION_START_CLIPBOARD_TIMER" - const val EXTRA_NOTIFICATION_TIME = "EXTRA_NOTIFICATION_TIME" - private const val ACTION_CLEAR = "ACTION_CLEAR_CLIPBOARD" - private const val CHANNEL_ID = "NotificationService" - // Newest Samsung phones now feature a history of up to 30 items. To err on the side of - // caution, - // push 35 fake ones. - private const val CLIPBOARD_CLEAR_COUNT = 35 - } -} diff --git a/app/src/main/java/dev/msfjarvis/aps/util/services/OreoAutofillService.kt b/app/src/main/java/dev/msfjarvis/aps/util/services/OreoAutofillService.kt deleted file mode 100644 index 3a1c69d3..00000000 --- a/app/src/main/java/dev/msfjarvis/aps/util/services/OreoAutofillService.kt +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ -package dev.msfjarvis.aps.util.services - -import android.content.Context -import android.os.Build -import android.os.CancellationSignal -import android.service.autofill.AutofillService -import android.service.autofill.FillCallback -import android.service.autofill.FillRequest -import android.service.autofill.FillResponse -import android.service.autofill.SaveCallback -import android.service.autofill.SaveRequest -import androidx.annotation.RequiresApi -import com.github.androidpasswordstore.autofillparser.AutofillScenario -import com.github.androidpasswordstore.autofillparser.Credentials -import com.github.androidpasswordstore.autofillparser.FillableForm -import com.github.androidpasswordstore.autofillparser.FixedSaveCallback -import com.github.androidpasswordstore.autofillparser.FormOrigin -import com.github.androidpasswordstore.autofillparser.cachePublicSuffixList -import com.github.androidpasswordstore.autofillparser.passwordValue -import com.github.androidpasswordstore.autofillparser.recoverNodes -import com.github.androidpasswordstore.autofillparser.usernameValue -import dagger.hilt.android.AndroidEntryPoint -import dev.msfjarvis.aps.BuildConfig -import dev.msfjarvis.aps.R -import dev.msfjarvis.aps.ui.autofill.AutofillSaveActivity -import dev.msfjarvis.aps.util.autofill.Api30AutofillResponseBuilder -import dev.msfjarvis.aps.util.autofill.AutofillResponseBuilder -import dev.msfjarvis.aps.util.extensions.getString -import dev.msfjarvis.aps.util.extensions.hasFlag -import dev.msfjarvis.aps.util.extensions.sharedPrefs -import dev.msfjarvis.aps.util.settings.PreferenceKeys -import javax.inject.Inject -import logcat.LogPriority.ERROR -import logcat.logcat - -@RequiresApi(26) -@AndroidEntryPoint -class OreoAutofillService : AutofillService() { - - companion object { - - // TODO: Provide a user-configurable denylist - private val DENYLISTED_PACKAGES = - listOf( - BuildConfig.APPLICATION_ID, - "android", - "com.android.settings", - "com.android.settings.intelligence", - "com.android.systemui", - "com.oneplus.applocker", - "org.sufficientlysecure.keychain", - ) - - private const val DISABLE_AUTOFILL_DURATION_MS = 1000 * 60 * 60 * 24L - } - - @Inject lateinit var api30ResponseBuilderFactory: Api30AutofillResponseBuilder.Factory - @Inject lateinit var responseBuilderFactory: AutofillResponseBuilder.Factory - - override fun onCreate() { - super.onCreate() - cachePublicSuffixList(applicationContext) - } - - override fun onFillRequest( - request: FillRequest, - cancellationSignal: CancellationSignal, - callback: FillCallback - ) { - val structure = - request.fillContexts.lastOrNull()?.structure - ?: run { - callback.onSuccess(null) - return - } - if (structure.activityComponent.packageName in DENYLISTED_PACKAGES) { - if (Build.VERSION.SDK_INT >= 28) { - callback.onSuccess( - FillResponse.Builder().run { - disableAutofill(DISABLE_AUTOFILL_DURATION_MS) - build() - } - ) - } else { - callback.onSuccess(null) - } - return - } - val formToFill = - FillableForm.parseAssistStructure( - this, - structure, - isManualRequest = request.flags hasFlag FillRequest.FLAG_MANUAL_REQUEST, - getCustomSuffixes(), - ) - ?: run { - logcat { "Form cannot be filled" } - callback.onSuccess(null) - return - } - if (Build.VERSION.SDK_INT >= 30) { - api30ResponseBuilderFactory - .create(formToFill) - .fillCredentials(this, request.inlineSuggestionsRequest, callback) - } else { - responseBuilderFactory.create(formToFill).fillCredentials(this, callback) - } - } - - override fun onSaveRequest(request: SaveRequest, callback: SaveCallback) { - // SaveCallback's behavior and feature set differs based on both target and device SDK, so - // we replace it with a wrapper that works the same in all situations. - @Suppress("NAME_SHADOWING") val callback = FixedSaveCallback(this, callback) - val structure = - request.fillContexts.lastOrNull()?.structure - ?: run { - callback.onFailure(getString(R.string.oreo_autofill_save_app_not_supported)) - return - } - val clientState = - request.clientState - ?: run { - logcat(ERROR) { "Received save request without client state" } - callback.onFailure(getString(R.string.oreo_autofill_save_internal_error)) - return - } - val scenario = - AutofillScenario.fromClientState(clientState)?.recoverNodes(structure) - ?: run { - logcat(ERROR) { "Failed to recover client state or nodes from client state" } - callback.onFailure(getString(R.string.oreo_autofill_save_internal_error)) - return - } - val formOrigin = - FormOrigin.fromBundle(clientState) - ?: run { - logcat(ERROR) { "Failed to recover form origin from client state" } - callback.onFailure(getString(R.string.oreo_autofill_save_internal_error)) - return - } - - val username = scenario.usernameValue - val password = - scenario.passwordValue - ?: run { - callback.onFailure(getString(R.string.oreo_autofill_save_passwords_dont_match)) - return - } - callback.onSuccess( - AutofillSaveActivity.makeSaveIntentSender( - this, - credentials = Credentials(username, password, null), - formOrigin = formOrigin - ) - ) - } -} - -fun Context.getDefaultUsername() = - sharedPrefs.getString(PreferenceKeys.OREO_AUTOFILL_DEFAULT_USERNAME) - -fun Context.getCustomSuffixes(): Sequence<String> { - return sharedPrefs - .getString(PreferenceKeys.OREO_AUTOFILL_CUSTOM_PUBLIC_SUFFIXES) - ?.splitToSequence('\n') - ?.filter { it.isNotBlank() && it.first() != '.' && it.last() != '.' } - ?: emptySequence() -} diff --git a/app/src/main/java/dev/msfjarvis/aps/util/services/PasswordExportService.kt b/app/src/main/java/dev/msfjarvis/aps/util/services/PasswordExportService.kt deleted file mode 100644 index 91a7ac5f..00000000 --- a/app/src/main/java/dev/msfjarvis/aps/util/services/PasswordExportService.kt +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright © 2014-2021 The Android Password Store Authors. All Rights Reserved. - * SPDX-License-Identifier: GPL-3.0-only - */ - -package dev.msfjarvis.aps.util.services - -import android.app.NotificationChannel -import android.app.NotificationManager -import android.app.Service -import android.content.Intent -import android.net.Uri -import android.os.Build -import android.os.IBinder -import androidx.core.app.NotificationCompat -import androidx.core.content.getSystemService -import androidx.documentfile.provider.DocumentFile -import dev.msfjarvis.aps.R -import dev.msfjarvis.aps.data.repo.PasswordRepository -import java.time.LocalDateTime -import java.time.format.DateTimeFormatter -import java.util.Calendar -import java.util.TimeZone -import logcat.logcat - -class PasswordExportService : Service() { - - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - if (intent != null) { - when (intent.action) { - ACTION_EXPORT_PASSWORD -> { - val uri = intent.getParcelableExtra<Uri>("uri") - if (uri != null) { - val targetDirectory = DocumentFile.fromTreeUri(applicationContext, uri) - - if (targetDirectory != null) { - createNotification() - exportPasswords(targetDirectory) - stopSelf() - return START_NOT_STICKY - } - } - } - } - } - return super.onStartCommand(intent, flags, startId) - } - - override fun onBind(intent: Intent?): IBinder? { - return null - } - - /** - * Exports passwords to the given directory. - * - * Recursively copies the existing password store to an external directory. - * - * @param targetDirectory directory to copy password directory to. - */ - private fun exportPasswords(targetDirectory: DocumentFile) { - - val repositoryDirectory = requireNotNull(PasswordRepository.getRepositoryDirectory()) - val sourcePassDir = DocumentFile.fromFile(repositoryDirectory) - - logcat { "Copying ${repositoryDirectory.path} to $targetDirectory" } - - val dateString = - if (Build.VERSION.SDK_INT >= 26) { - LocalDateTime.now().format(DateTimeFormatter.ISO_DATE_TIME) - } else { - String.format("%tFT%<tRZ", Calendar.getInstance(TimeZone.getTimeZone("Z"))) - } - - val passDir = targetDirectory.createDirectory("password_store_$dateString") - - if (passDir != null) { - copyDirToDir(sourcePassDir, passDir) - } - } - - /** - * Copies a password file to a given directory. - * - * Note: this does not preserve last modified time. - * - * @param passwordFile password file to copy. - * @param targetDirectory target directory to copy password. - */ - private fun copyFileToDir(passwordFile: DocumentFile, targetDirectory: DocumentFile) { - val sourceInputStream = contentResolver.openInputStream(passwordFile.uri) - val name = passwordFile.name - val targetPasswordFile = targetDirectory.createFile("application/octet-stream", name!!) - if (targetPasswordFile?.exists() == true) { - val destOutputStream = contentResolver.openOutputStream(targetPasswordFile.uri) - - if (destOutputStream != null && sourceInputStream != null) { - sourceInputStream.copyTo(destOutputStream, 1024) - - sourceInputStream.close() - destOutputStream.close() - } - } - } - - /** - * Recursively copies a directory to a destination. - * - * @param sourceDirectory directory to copy from. - * @param targetDirectory directory to copy to. - */ - private fun copyDirToDir(sourceDirectory: DocumentFile, targetDirectory: DocumentFile) { - sourceDirectory.listFiles().forEach { file -> - if (file.isDirectory) { - // Create new directory and recurse - val newDir = targetDirectory.createDirectory(file.name!!) - copyDirToDir(file, newDir!!) - } else { - copyFileToDir(file, targetDirectory) - } - } - } - - private fun createNotification() { - createNotificationChannel() - - val notification = - NotificationCompat.Builder(this, CHANNEL_ID) - .setContentTitle(getString(R.string.app_name)) - .setContentText(getString(R.string.exporting_passwords)) - .setSmallIcon(R.drawable.ic_round_import_export) - .setPriority(NotificationCompat.PRIORITY_LOW) - .build() - - startForeground(2, notification) - } - - private fun createNotificationChannel() { - if (Build.VERSION.SDK_INT >= 26) { - val serviceChannel = - NotificationChannel( - CHANNEL_ID, - getString(R.string.app_name), - NotificationManager.IMPORTANCE_LOW - ) - val manager = getSystemService<NotificationManager>() - if (manager != null) { - manager.createNotificationChannel(serviceChannel) - } else { - logcat { "Failed to create notification channel" } - } - } - } - - companion object { - - const val ACTION_EXPORT_PASSWORD = "ACTION_EXPORT_PASSWORD" - private const val CHANNEL_ID = "NotificationService" - } -} |
