Merge remote-tracking branch 'origin/master' into dev

This commit is contained in:
Tobias Kaminsky 2024-04-05 02:31:01 +02:00
commit 36987463e5
26 changed files with 152 additions and 115 deletions

View file

@ -115,8 +115,10 @@ class OfflineSyncWork constructor(
* @return new etag if changed, `null` otherwise * @return new etag if changed, `null` otherwise
*/ */
private fun checkEtagChanged(folderName: String, storageManager: FileDataStorageManager, user: User): String? { private fun checkEtagChanged(folderName: String, storageManager: FileDataStorageManager, user: User): String? {
val ocFolder = storageManager.getFileByPath(folderName) val ocFolder = storageManager.getFileByPath(folderName) ?: return null
Log_OC.d(TAG, folderName + ": currentEtag: " + ocFolder.etag)
Log_OC.d(TAG, "$folderName: currentEtag: ${ocFolder.etag}")
// check for etag change, if false, skip // check for etag change, if false, skip
val checkEtagOperation = CheckEtagRemoteOperation( val checkEtagOperation = CheckEtagRemoteOperation(
ocFolder.remotePath, ocFolder.remotePath,

View file

@ -49,9 +49,9 @@ class FileDownloadHelper {
val fileStorageManager = FileDataStorageManager(user, MainApp.getAppContext().contentResolver) val fileStorageManager = FileDataStorageManager(user, MainApp.getAppContext().contentResolver)
val topParentId = fileStorageManager.getTopParentId(file) val topParentId = fileStorageManager.getTopParentId(file)
return if (file.isFolder) { val isJobScheduled = backgroundJobManager.isStartFileDownloadJobScheduled(user, file.fileId)
backgroundJobManager.isStartFileDownloadJobScheduled(user, file.fileId) || return isJobScheduled || if (file.isFolder) {
backgroundJobManager.isStartFileDownloadJobScheduled(user, topParentId) backgroundJobManager.isStartFileDownloadJobScheduled(user, topParentId)
} else { } else {
FileDownloadWorker.isDownloading(user.accountName, file.fileId) FileDownloadWorker.isDownloading(user.accountName, file.fileId)
} }

View file

@ -235,7 +235,7 @@ class FileUploadWorker(
} }
private fun cleanupUploadProcess(result: RemoteOperationResult<Any?>, operation: UploadFileOperation) { private fun cleanupUploadProcess(result: RemoteOperationResult<Any?>, operation: UploadFileOperation) {
if (!(isStopped && result.isCancelled)) { if (!isStopped || !result.isCancelled) {
uploadsStorageManager.updateDatabaseUploadResult(result, operation) uploadsStorageManager.updateDatabaseUploadResult(result, operation)
notifyUploadResult(operation, result) notifyUploadResult(operation, result)
notificationManager.dismissWorkerNotifications() notificationManager.dismissWorkerNotifications()
@ -288,17 +288,18 @@ class FileUploadWorker(
context.resources context.resources
) )
// FIXME SYNC_CONFLICT passes wrong OCFile, check ConflictsResolveActivity.createIntent usage
val conflictResolveIntent = if (uploadResult.code == ResultCode.SYNC_CONFLICT) { val conflictResolveIntent = if (uploadResult.code == ResultCode.SYNC_CONFLICT) {
intents.conflictResolveActionIntents(context, uploadFileOperation) intents.conflictResolveActionIntents(context, uploadFileOperation)
} else { } else {
null null
} }
val credentialIntent: PendingIntent? = if (uploadResult.code == ResultCode.UNAUTHORIZED) { val credentialIntent: PendingIntent? = if (uploadResult.code == ResultCode.UNAUTHORIZED) {
intents.credentialIntent(uploadFileOperation) intents.credentialIntent(uploadFileOperation)
} else { } else {
null null
} }
notifyForFailedResult(uploadResult.code, conflictResolveIntent, credentialIntent, errorMessage) notifyForFailedResult(uploadResult.code, conflictResolveIntent, credentialIntent, errorMessage)
showNewNotification(uploadFileOperation) showNewNotification(uploadFileOperation)
} }

View file

@ -113,6 +113,9 @@ import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatDelegate; import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.util.Pair; import androidx.core.util.Pair;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleEventObserver;
import androidx.lifecycle.ProcessLifecycleOwner;
import androidx.multidex.MultiDexApplication; import androidx.multidex.MultiDexApplication;
import dagger.android.AndroidInjector; import dagger.android.AndroidInjector;
import dagger.android.DispatchingAndroidInjector; import dagger.android.DispatchingAndroidInjector;
@ -297,6 +300,8 @@ public class MainApp extends MultiDexApplication implements HasAndroidInjector {
setAppTheme(preferences.getDarkThemeMode()); setAppTheme(preferences.getDarkThemeMode());
super.onCreate(); super.onCreate();
ProcessLifecycleOwner.get().getLifecycle().addObserver(lifecycleEventObserver);
insertConscrypt(); insertConscrypt();
initSecurityKeyManager(); initSecurityKeyManager();
@ -362,6 +367,15 @@ public class MainApp extends MultiDexApplication implements HasAndroidInjector {
registerGlobalPassCodeProtection(); registerGlobalPassCodeProtection();
} }
private final LifecycleEventObserver lifecycleEventObserver = ((lifecycleOwner, event) -> {
if (event == Lifecycle.Event.ON_START) {
Log_OC.d(TAG, "APP IN FOREGROUND");
} else if (event == Lifecycle.Event.ON_STOP) {
passCodeManager.setCanAskPin(true);
Log_OC.d(TAG, "APP IN BACKGROUND");
}
});
private void registerGlobalPassCodeProtection() { private void registerGlobalPassCodeProtection() {
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {

View file

@ -49,12 +49,10 @@ class PassCodeManager(private val preferences: AppPreferences, private val clock
* the pass code being requested on screen rotations. * the pass code being requested on screen rotations.
*/ */
private const val PASS_CODE_TIMEOUT = 5000 private const val PASS_CODE_TIMEOUT = 5000
private const val TAG = "PassCodeManager"
} }
private var visibleActivitiesCounter = 0 var canAskPin = true
private var lastResumedActivity: Activity? = null private var askPinWhenDeviceLocked = false
private fun isExemptActivity(activity: Activity): Boolean { private fun isExemptActivity(activity: Activity): Boolean {
return exemptOfPasscodeActivities.contains(activity.javaClass) return exemptOfPasscodeActivities.contains(activity.javaClass)
@ -69,7 +67,7 @@ class PassCodeManager(private val preferences: AppPreferences, private val clock
val passcodeRequested = passCodeShouldBeRequested(timestamp) val passcodeRequested = passCodeShouldBeRequested(timestamp)
val credentialsRequested = deviceCredentialsShouldBeRequested(timestamp, activity) val credentialsRequested = deviceCredentialsShouldBeRequested(timestamp, activity)
val shouldHideView = passcodeRequested || credentialsRequested val shouldHideView = passcodeRequested || credentialsRequested
toggleActivityVisibility(shouldHideView, activity) getActivityRootView(activity)?.visibility = if (shouldHideView) View.GONE else View.VISIBLE
askedForPin = shouldHideView askedForPin = shouldHideView
if (passcodeRequested) { if (passcodeRequested) {
@ -82,47 +80,16 @@ class PassCodeManager(private val preferences: AppPreferences, private val clock
} }
} }
if (!askedForPin && preferences.lockTimestamp != 0L) { if (!askedForPin && preferences.lockTimestamp != 0L || askPinWhenDeviceLocked) {
updateLockTimestamp() updateLockTimestamp()
} askPinWhenDeviceLocked = false
if (!isExemptActivity(activity)) {
addVisibleActivity(activity) // keep it AFTER passCodeShouldBeRequested was checked
} }
return askedForPin return askedForPin
} }
/**
* Used to hide root view while transitioning to passcode activity
*/
private fun toggleActivityVisibility(
hide: Boolean,
activity: Activity
) {
if (hide) {
getActivityRootView(activity)?.visibility = View.GONE
} else {
getActivityRootView(activity)?.visibility = View.VISIBLE
}
}
private fun addVisibleActivity(activity: Activity) {
// don't count the same activity twice
if (lastResumedActivity != activity) {
visibleActivitiesCounter++
lastResumedActivity = activity
}
}
private fun removeVisibleActivity() {
visibleActivitiesCounter--
lastResumedActivity = null
}
private fun setSecureFlag(activity: Activity) { private fun setSecureFlag(activity: Activity) {
val window = activity.window activity.window?.let { window ->
if (window != null) {
if (isPassCodeEnabled() || deviceCredentialsAreEnabled(activity)) { if (isPassCodeEnabled() || deviceCredentialsAreEnabled(activity)) {
window.addFlags(WindowManager.LayoutParams.FLAG_SECURE) window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
} else { } else {
@ -132,38 +99,38 @@ class PassCodeManager(private val preferences: AppPreferences, private val clock
} }
private fun requestPasscode(activity: Activity) { private fun requestPasscode(activity: Activity) {
val i = Intent(MainApp.getAppContext(), PassCodeActivity::class.java) val i = Intent(MainApp.getAppContext(), PassCodeActivity::class.java).apply {
i.action = PassCodeActivity.ACTION_CHECK action = PassCodeActivity.ACTION_CHECK
i.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
}
activity.startActivityForResult(i, PASSCODE_ACTIVITY) activity.startActivityForResult(i, PASSCODE_ACTIVITY)
} }
private fun requestCredentials(activity: Activity) { private fun requestCredentials(activity: Activity) {
val i = Intent(MainApp.getAppContext(), RequestCredentialsActivity::class.java) val i = Intent(MainApp.getAppContext(), RequestCredentialsActivity::class.java).apply {
i.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
}
activity.startActivityForResult(i, PASSCODE_ACTIVITY) activity.startActivityForResult(i, PASSCODE_ACTIVITY)
} }
fun onActivityStopped(activity: Activity) { fun onActivityStopped(activity: Activity) {
if (visibleActivitiesCounter > 0 && !isExemptActivity(activity)) {
removeVisibleActivity()
}
val powerMgr = activity.getSystemService(Context.POWER_SERVICE) as PowerManager val powerMgr = activity.getSystemService(Context.POWER_SERVICE) as PowerManager
if ((isPassCodeEnabled() || deviceCredentialsAreEnabled(activity)) && !powerMgr.isScreenOn) { if ((isPassCodeEnabled() || deviceCredentialsAreEnabled(activity)) && !powerMgr.isInteractive) {
activity.moveTaskToBack(true) askPinWhenDeviceLocked = true
} }
} }
fun updateLockTimestamp() { fun updateLockTimestamp() {
preferences.lockTimestamp = clock.millisSinceBoot preferences.lockTimestamp = clock.millisSinceBoot
canAskPin = false
} }
/** /**
* `true` if the time elapsed since last unlock is longer than [PASS_CODE_TIMEOUT] and no activities are visible * `true` if the time elapsed since last unlock is longer than [PASS_CODE_TIMEOUT] and no activities are visible
*/ */
private fun shouldBeLocked(timestamp: Long) = private fun shouldBeLocked(timestamp: Long): Boolean {
abs(clock.millisSinceBoot - timestamp) > PASS_CODE_TIMEOUT && return (abs(clock.millisSinceBoot - timestamp) > PASS_CODE_TIMEOUT && canAskPin) || askPinWhenDeviceLocked
visibleActivitiesCounter <= 0 }
@VisibleForTesting @VisibleForTesting
fun passCodeShouldBeRequested(timestamp: Long): Boolean { fun passCodeShouldBeRequested(timestamp: Long): Boolean {

View file

@ -361,15 +361,6 @@ public class OCFile implements Parcelable, Comparable<OCFile>, ServerFileInterfa
return false; return false;
} }
public String getFileNameWithoutExtension(String fileName) {
int dotIndex = fileName.lastIndexOf('.');
if (dotIndex > 0) {
return fileName.substring(0, dotIndex);
} else {
return fileName;
}
}
/** /**
* The path, where the file is stored locally * The path, where the file is stored locally
* *

View file

@ -273,7 +273,14 @@ public class DownloadFileOperation extends RemoteOperation {
} }
} }
if (downloadType == DownloadType.EXPORT) { if (downloadType == DownloadType.DOWNLOAD && !file.isEncrypted()) {
moved = tmpFile.renameTo(newFile);
boolean isLastModifiedSet = newFile.setLastModified(file.getModificationTimestamp());
Log_OC.d(TAG, "Last modified set: " + isLastModifiedSet);
if (!moved) {
result = new RemoteOperationResult(RemoteOperationResult.ResultCode.LOCAL_STORAGE_NOT_MOVED);
}
} else if (downloadType == DownloadType.EXPORT) {
new FileExportUtils().exportFile(file.getFileName(), new FileExportUtils().exportFile(file.getFileName(),
file.getMimeType(), file.getMimeType(),
operationContext.getContentResolver(), operationContext.getContentResolver(),

View file

@ -23,6 +23,7 @@ package com.owncloud.android.operations;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.text.TextUtils; import android.text.TextUtils;
@ -98,6 +99,9 @@ import javax.crypto.Cipher;
import androidx.annotation.CheckResult; import androidx.annotation.CheckResult;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import static com.owncloud.android.ui.activity.FileDisplayActivity.REFRESH_FOLDER_EVENT_RECEIVER;
/** /**
@ -455,6 +459,7 @@ public class UploadFileOperation extends SyncOperation {
boolean metadataExists = false; boolean metadataExists = false;
String token = null; String token = null;
Object object = null;
ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(getContext()); ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(getContext());
String publicKey = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.PUBLIC_KEY); String publicKey = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.PUBLIC_KEY);
@ -466,6 +471,7 @@ public class UploadFileOperation extends SyncOperation {
if (result != null) { if (result != null) {
return result; return result;
} }
/***** E2E *****/ /***** E2E *****/
// Only on V2+: whenever we change something, increase counter // Only on V2+: whenever we change something, increase counter
long counter = -1; long counter = -1;
@ -485,13 +491,7 @@ public class UploadFileOperation extends SyncOperation {
// Update metadata // Update metadata
EncryptionUtilsV2 encryptionUtilsV2 = new EncryptionUtilsV2(); EncryptionUtilsV2 encryptionUtilsV2 = new EncryptionUtilsV2();
// kotlin.Pair<Boolean, DecryptedFolderMetadataFile> metadataPair = object = EncryptionUtils.downloadFolderMetadata(parentFile, client, mContext, user);
// encryptionUtilsV2.retrieveMetadata(parentFile,
// client,
// user,
// mContext);
Object object = EncryptionUtils.downloadFolderMetadata(parentFile, client, mContext, user);
if (object instanceof DecryptedFolderMetadataFileV1 decrypted && decrypted.getMetadata() != null) { if (object instanceof DecryptedFolderMetadataFileV1 decrypted && decrypted.getMetadata() != null) {
metadataExists = true; metadataExists = true;
} }
@ -703,13 +703,6 @@ public class UploadFileOperation extends SyncOperation {
"", "",
arbitraryDataProvider, arbitraryDataProvider,
user); user);
// unlock
result = EncryptionUtils.unlockFolderV1(parentFile, client, token);
if (result.isSuccess()) {
token = null;
}
} else { } else {
DecryptedFolderMetadataFile metadata = (DecryptedFolderMetadataFile) object; DecryptedFolderMetadataFile metadata = (DecryptedFolderMetadataFile) object;
encryptionUtilsV2.addFileToMetadata( encryptionUtilsV2.addFileToMetadata(
@ -726,17 +719,10 @@ public class UploadFileOperation extends SyncOperation {
metadata, metadata,
token, token,
client, client,
metadataExists, true,
mContext, mContext,
user, user,
getStorageManager()); getStorageManager());
// unlock
result = EncryptionUtils.unlockFolder(parentFile, client, token);
if (result.isSuccess()) {
token = null;
}
} }
encryptedTempFile.delete(); encryptedTempFile.delete();
@ -751,6 +737,7 @@ public class UploadFileOperation extends SyncOperation {
result = new RemoteOperationResult(e); result = new RemoteOperationResult(e);
} finally { } finally {
mUploadStarted.set(false); mUploadStarted.set(false);
sendRefreshFolderEventBroadcast();
if (fileLock != null) { if (fileLock != null) {
try { try {
@ -768,6 +755,18 @@ public class UploadFileOperation extends SyncOperation {
} }
logResult(result, mFile.getStoragePath(), mFile.getRemotePath()); logResult(result, mFile.getStoragePath(), mFile.getRemotePath());
// Unlock must be done otherwise folder stays locked and user can't upload any file
RemoteOperationResult<Void> unlockFolderResult;
if (object instanceof DecryptedFolderMetadataFileV1) {
unlockFolderResult = EncryptionUtils.unlockFolderV1(parentFile, client, token);
} else {
unlockFolderResult = EncryptionUtils.unlockFolder(parentFile, client, token);
}
if (unlockFolderResult != null && !unlockFolderResult.isSuccess()) {
result = unlockFolderResult;
}
} }
if (result.isSuccess()) { if (result.isSuccess()) {
@ -776,17 +775,6 @@ public class UploadFileOperation extends SyncOperation {
getStorageManager().saveConflict(mFile, mFile.getEtagInConflict()); getStorageManager().saveConflict(mFile, mFile.getEtagInConflict());
} }
// unlock must be done always
if (token != null) {
RemoteOperationResult unlockFolderResult = EncryptionUtils.unlockFolder(parentFile,
client,
token);
if (!unlockFolderResult.isSuccess()) {
return unlockFolderResult;
}
}
// delete temporal file // delete temporal file
if (temporalFile != null && temporalFile.exists() && !temporalFile.delete()) { if (temporalFile != null && temporalFile.exists() && !temporalFile.delete()) {
Log_OC.e(TAG, "Could not delete temporal file " + temporalFile.getAbsolutePath()); Log_OC.e(TAG, "Could not delete temporal file " + temporalFile.getAbsolutePath());
@ -795,6 +783,11 @@ public class UploadFileOperation extends SyncOperation {
return result; return result;
} }
private void sendRefreshFolderEventBroadcast() {
Intent intent = new Intent(REFRESH_FOLDER_EVENT_RECEIVER);
LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
}
private RemoteOperationResult checkConditions(File originalFile) { private RemoteOperationResult checkConditions(File originalFile) {
RemoteOperationResult remoteOperationResult = null; RemoteOperationResult remoteOperationResult = null;

View file

@ -69,6 +69,7 @@ import com.nextcloud.client.jobs.download.FileDownloadHelper;
import com.nextcloud.client.jobs.download.FileDownloadWorker; import com.nextcloud.client.jobs.download.FileDownloadWorker;
import com.nextcloud.client.jobs.upload.FileUploadHelper; import com.nextcloud.client.jobs.upload.FileUploadHelper;
import com.nextcloud.client.jobs.upload.FileUploadWorker; import com.nextcloud.client.jobs.upload.FileUploadWorker;
import com.nextcloud.client.jobs.upload.UploadNotificationManager;
import com.nextcloud.client.media.PlayerServiceConnection; import com.nextcloud.client.media.PlayerServiceConnection;
import com.nextcloud.client.network.ClientFactory; import com.nextcloud.client.network.ClientFactory;
import com.nextcloud.client.network.ConnectivityService; import com.nextcloud.client.network.ConnectivityService;
@ -229,6 +230,8 @@ public class FileDisplayActivity extends FileActivity
public static final String KEY_IS_SEARCH_OPEN = "IS_SEARCH_OPEN"; public static final String KEY_IS_SEARCH_OPEN = "IS_SEARCH_OPEN";
public static final String KEY_SEARCH_QUERY = "SEARCH_QUERY"; public static final String KEY_SEARCH_QUERY = "SEARCH_QUERY";
public static final String REFRESH_FOLDER_EVENT_RECEIVER = "REFRESH_FOLDER_EVENT";
private String searchQuery = ""; private String searchQuery = "";
private boolean searchOpen; private boolean searchOpen;
@ -236,6 +239,7 @@ public class FileDisplayActivity extends FileActivity
private PlayerServiceConnection mPlayerConnection; private PlayerServiceConnection mPlayerConnection;
private Optional<User> lastDisplayedUser = Optional.empty(); private Optional<User> lastDisplayedUser = Optional.empty();
private int menuItemId = -1; private int menuItemId = -1;
@Inject AppPreferences preferences; @Inject AppPreferences preferences;
@Inject AppInfo appInfo; @Inject AppInfo appInfo;
@ -283,6 +287,7 @@ public class FileDisplayActivity extends FileActivity
initSyncBroadcastReceiver(); initSyncBroadcastReceiver();
observeWorkerState(); observeWorkerState();
registerRefreshFolderEventReceiver();
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -2296,6 +2301,24 @@ public class FileDisplayActivity extends FileActivity
checkForNewDevVersionNecessary(getApplicationContext()); checkForNewDevVersionNecessary(getApplicationContext());
} }
private void registerRefreshFolderEventReceiver() {
IntentFilter filter = new IntentFilter(REFRESH_FOLDER_EVENT_RECEIVER);
LocalBroadcastManager.getInstance(this).registerReceiver(refreshFolderEventReceiver, filter);
}
private final BroadcastReceiver refreshFolderEventReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
syncAndUpdateFolder(true);
}
};
@Override
protected void onDestroy() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(refreshFolderEventReceiver);
super.onDestroy();
}
@Override @Override
protected void onRestart() { protected void onRestart() {
super.onRestart(); super.onRestart();

View file

@ -106,11 +106,9 @@ public class RemoveFilesDialogFragment extends ConfirmationDialogFragment implem
if (containsFolder || containsDown) { if (containsFolder || containsDown) {
args.putInt(ARG_NEGATIVE_BTN_RES, R.string.confirmation_remove_local); args.putInt(ARG_NEGATIVE_BTN_RES, R.string.confirmation_remove_local);
args.putInt(ARG_NEUTRAL_BTN_RES, R.string.file_keep);
} else {
args.putInt(ARG_NEGATIVE_BTN_RES, R.string.file_keep);
} }
args.putInt(ARG_NEUTRAL_BTN_RES, R.string.file_keep);
args.putParcelableArrayList(ARG_TARGET_FILES, files); args.putParcelableArrayList(ARG_TARGET_FILES, files);
frag.setArguments(args); frag.setArguments(args);

View file

@ -22,13 +22,11 @@
package com.owncloud.android.utils; package com.owncloud.android.utils;
import android.content.Context; import android.content.Context;
import android.os.Build;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Base64; import android.util.Base64;
import android.util.Pair; import android.util.Pair;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.primitives.Bytes;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken; import com.google.gson.reflect.TypeToken;
@ -73,17 +71,14 @@ import org.apache.commons.httpclient.HttpStatus;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.math.BigInteger; import java.math.BigInteger;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.security.InvalidAlgorithmParameterException; import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.security.Key; import java.security.Key;
@ -114,7 +109,6 @@ import java.util.UUID;
import javax.crypto.BadPaddingException; import javax.crypto.BadPaddingException;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream; import javax.crypto.CipherOutputStream;
import javax.crypto.IllegalBlockSizeException; import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator; import javax.crypto.KeyGenerator;
@ -127,7 +121,6 @@ import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
@ -516,8 +509,12 @@ public final class EncryptionUtils {
new TypeToken<>() { new TypeToken<>() {
}); });
if ("2.0".equals(v2.getVersion()) || "2".equals(v2.getVersion())) { if (v2 != null) {
return E2EVersion.V2_0; if ("2.0".equals(v2.getVersion()) || "2".equals(v2.getVersion())) {
return E2EVersion.V2_0;
}
} else {
return E2EVersion.UNKNOWN;
} }
} }
@ -565,7 +562,8 @@ public final class EncryptionUtils {
} }
public static EncryptedFile encryptFile(File file, Cipher cipher) throws InvalidParameterSpecException { public static EncryptedFile encryptFile(File file, Cipher cipher) throws InvalidParameterSpecException {
File encryptedFile = new File(file.getAbsolutePath() + ".enc"); // FIXME this won't work on low or write-protected storage
File encryptedFile = new File(file.getAbsolutePath() + ".enc.jpg");
encryptFileWithGivenCipher(file, encryptedFile, cipher); encryptFileWithGivenCipher(file, encryptedFile, cipher);
String authenticationTagString = getAuthenticationTag(cipher); String authenticationTagString = getAuthenticationTag(cipher);
return new EncryptedFile(encryptedFile, authenticationTagString); return new EncryptedFile(encryptedFile, authenticationTagString);

View file

@ -41,9 +41,12 @@
<string name="assistant_screen_create_task_alert_dialog_input_field_placeholder">أكتُب أيَّ نص</string> <string name="assistant_screen_create_task_alert_dialog_input_field_placeholder">أكتُب أيَّ نص</string>
<string name="assistant_screen_delete_task_alert_dialog_description">هل أنت متأكد أنك ترغب بحذف هذه المهمة؟</string> <string name="assistant_screen_delete_task_alert_dialog_description">هل أنت متأكد أنك ترغب بحذف هذه المهمة؟</string>
<string name="assistant_screen_delete_task_alert_dialog_title">حذف مهمة</string> <string name="assistant_screen_delete_task_alert_dialog_title">حذف مهمة</string>
<string name="assistant_screen_failed_task_text">فشل</string>
<string name="assistant_screen_loading">يتم الآن تحميل قائمة المهام؛ يرجى الانتظار ...</string> <string name="assistant_screen_loading">يتم الآن تحميل قائمة المهام؛ يرجى الانتظار ...</string>
<string name="assistant_screen_no_task_available_for_all_task_filter_text">لا توجد أي مهام متوفرة. حدِّد نوع المهمة لإنشاء واحدة جديدة.</string> <string name="assistant_screen_no_task_available_for_all_task_filter_text">لا توجد أي مهام متوفرة. حدِّد نوع المهمة لإنشاء واحدة جديدة.</string>
<string name="assistant_screen_no_task_available_text">لا توجد أي مهام متوفرة من النوع %s. يمكنك إنشاء مهمة جديدة من الشريط السفلي الجانبي.</string> <string name="assistant_screen_no_task_available_text">لا توجد أي مهام متوفرة من النوع %s. يمكنك إنشاء مهمة جديدة من الشريط السفلي الجانبي.</string>
<string name="assistant_screen_scheduled_task_status_text">مُجدول</string>
<string name="assistant_screen_successful_task_text">مُكتمل</string>
<string name="assistant_screen_task_create_fail_message">حدث خطأ أثناء إنشاء المهمة</string> <string name="assistant_screen_task_create_fail_message">حدث خطأ أثناء إنشاء المهمة</string>
<string name="assistant_screen_task_create_success_message">تمّ إنشاء المهمة بنجاحٍ</string> <string name="assistant_screen_task_create_success_message">تمّ إنشاء المهمة بنجاحٍ</string>
<string name="assistant_screen_task_delete_fail_message">تمّ حذف المهمة بنجاح</string> <string name="assistant_screen_task_delete_fail_message">تمّ حذف المهمة بنجاح</string>
@ -52,6 +55,7 @@
<string name="assistant_screen_task_more_actions_bottom_sheet_delete_action">حذف مهمة</string> <string name="assistant_screen_task_more_actions_bottom_sheet_delete_action">حذف مهمة</string>
<string name="assistant_screen_task_types_error_state_message">تعذّر جلب أنواع المهام. قم رجاءً بالتحقُّق من اتصالك بالإنترنت.</string> <string name="assistant_screen_task_types_error_state_message">تعذّر جلب أنواع المهام. قم رجاءً بالتحقُّق من اتصالك بالإنترنت.</string>
<string name="assistant_screen_top_bar_title">المُساعِد</string> <string name="assistant_screen_top_bar_title">المُساعِد</string>
<string name="assistant_screen_unknown_task_status_text">غير معروف</string>
<string name="associated_account_not_found">الحساب المرتبط غير موجود!</string> <string name="associated_account_not_found">الحساب المرتبط غير موجود!</string>
<string name="auth_access_failed">فشل الوصول لـ: %1$s</string> <string name="auth_access_failed">فشل الوصول لـ: %1$s</string>
<string name="auth_account_does_not_exist">هذا الحساب لم تتم إضافته في هذا الجهاز بعد</string> <string name="auth_account_does_not_exist">هذا الحساب لم تتم إضافته في هذا الجهاز بعد</string>

View file

@ -45,6 +45,8 @@
<string name="assistant_screen_loading">Task List are loading, please wait</string> <string name="assistant_screen_loading">Task List are loading, please wait</string>
<string name="assistant_screen_no_task_available_for_all_task_filter_text">No task available. Select a task type to create a new task.</string> <string name="assistant_screen_no_task_available_for_all_task_filter_text">No task available. Select a task type to create a new task.</string>
<string name="assistant_screen_no_task_available_text">No task available for %s task type, you can create a new task from bottom right.</string> <string name="assistant_screen_no_task_available_text">No task available for %s task type, you can create a new task from bottom right.</string>
<string name="assistant_screen_scheduled_task_status_text">Scheduled</string>
<string name="assistant_screen_successful_task_text">Completed</string>
<string name="assistant_screen_task_create_fail_message">An error occurred while creating the task</string> <string name="assistant_screen_task_create_fail_message">An error occurred while creating the task</string>
<string name="assistant_screen_task_create_success_message">Task successfully created</string> <string name="assistant_screen_task_create_success_message">Task successfully created</string>
<string name="assistant_screen_task_delete_fail_message">Task successfully deleted</string> <string name="assistant_screen_task_delete_fail_message">Task successfully deleted</string>
@ -53,6 +55,7 @@
<string name="assistant_screen_task_more_actions_bottom_sheet_delete_action">Delete Task</string> <string name="assistant_screen_task_more_actions_bottom_sheet_delete_action">Delete Task</string>
<string name="assistant_screen_task_types_error_state_message">Unable to fetch task types, please check your internet connection.</string> <string name="assistant_screen_task_types_error_state_message">Unable to fetch task types, please check your internet connection.</string>
<string name="assistant_screen_top_bar_title">Assistant</string> <string name="assistant_screen_top_bar_title">Assistant</string>
<string name="assistant_screen_unknown_task_status_text">Unknown</string>
<string name="associated_account_not_found">Associated account not found!</string> <string name="associated_account_not_found">Associated account not found!</string>
<string name="auth_access_failed">Access failed: %1$s</string> <string name="auth_access_failed">Access failed: %1$s</string>
<string name="auth_account_does_not_exist">The account is not added on this device yet</string> <string name="auth_account_does_not_exist">The account is not added on this device yet</string>

View file

@ -36,7 +36,11 @@
<string name="app_widget_description">Показва един изпълним модул от таблото за управление</string> <string name="app_widget_description">Показва един изпълним модул от таблото за управление</string>
<string name="appbar_search_in">Търсене в %s</string> <string name="appbar_search_in">Търсене в %s</string>
<string name="assistant_screen_all_task_type">Всички</string> <string name="assistant_screen_all_task_type">Всички</string>
<string name="assistant_screen_failed_task_text">Неуспешно</string>
<string name="assistant_screen_scheduled_task_status_text">Планирано</string>
<string name="assistant_screen_successful_task_text">Завършен</string>
<string name="assistant_screen_task_delete_fail_message">Задачата е успешно изтрита</string> <string name="assistant_screen_task_delete_fail_message">Задачата е успешно изтрита</string>
<string name="assistant_screen_unknown_task_status_text">Неизвестен</string>
<string name="associated_account_not_found">Свързания профил не е намерен!</string> <string name="associated_account_not_found">Свързания профил не е намерен!</string>
<string name="auth_access_failed">Достъп неуспешен: %1$s</string> <string name="auth_access_failed">Достъп неуспешен: %1$s</string>
<string name="auth_account_does_not_exist">Профилът все още не съществува на устройството</string> <string name="auth_account_does_not_exist">Профилът все още не съществува на устройството</string>

View file

@ -32,6 +32,8 @@
<string name="allow_resharing">Aotrea an adrannañ</string> <string name="allow_resharing">Aotrea an adrannañ</string>
<string name="appbar_search_in">Klask e %s</string> <string name="appbar_search_in">Klask e %s</string>
<string name="assistant_screen_all_task_type">Pep tra</string> <string name="assistant_screen_all_task_type">Pep tra</string>
<string name="assistant_screen_successful_task_text">Achuet</string>
<string name="assistant_screen_unknown_task_status_text">Dianv</string>
<string name="associated_account_not_found">N\'eo ket bet kavet ur c\'hont kenstaget</string> <string name="associated_account_not_found">N\'eo ket bet kavet ur c\'hont kenstaget</string>
<string name="auth_access_failed">Aksed c\'hwitet: %1$s</string> <string name="auth_access_failed">Aksed c\'hwitet: %1$s</string>
<string name="auth_account_does_not_exist">Ar c\'hont n\'eo ket bet ouzhpennet war an ardivink c\'hoaz</string> <string name="auth_account_does_not_exist">Ar c\'hont n\'eo ket bet ouzhpennet war an ardivink c\'hoaz</string>

View file

@ -36,6 +36,10 @@
<string name="appbar_search_in">Cerca a %s</string> <string name="appbar_search_in">Cerca a %s</string>
<string name="assistant_screen_all_task_type">Totes</string> <string name="assistant_screen_all_task_type">Totes</string>
<string name="assistant_screen_create_task_alert_dialog_input_field_placeholder">Escriu una mica de text</string> <string name="assistant_screen_create_task_alert_dialog_input_field_placeholder">Escriu una mica de text</string>
<string name="assistant_screen_failed_task_text">Ha fallat</string>
<string name="assistant_screen_scheduled_task_status_text">Planificat</string>
<string name="assistant_screen_successful_task_text">S\'ha completat</string>
<string name="assistant_screen_unknown_task_status_text">Desconegut</string>
<string name="associated_account_not_found">No s\'ha trobat el compte associat!</string> <string name="associated_account_not_found">No s\'ha trobat el compte associat!</string>
<string name="auth_access_failed">No s\'ha tingut accés: %1$s</string> <string name="auth_access_failed">No s\'ha tingut accés: %1$s</string>
<string name="auth_account_does_not_exist">Encara no s\'ha afegit el compte en aquest dispositiu</string> <string name="auth_account_does_not_exist">Encara no s\'ha afegit el compte en aquest dispositiu</string>

View file

@ -38,7 +38,11 @@
<string name="appbar_search_in">Hledat v %s</string> <string name="appbar_search_in">Hledat v %s</string>
<string name="assistant_screen_all_task_type">Vše</string> <string name="assistant_screen_all_task_type">Vše</string>
<string name="assistant_screen_create_task_alert_dialog_input_field_placeholder">Zadejte nějaký text</string> <string name="assistant_screen_create_task_alert_dialog_input_field_placeholder">Zadejte nějaký text</string>
<string name="assistant_screen_failed_task_text">Nezdařilo se</string>
<string name="assistant_screen_scheduled_task_status_text">Naplánováno</string>
<string name="assistant_screen_successful_task_text">Dokončeno</string>
<string name="assistant_screen_task_delete_fail_message">Úloha úspěšně smazána</string> <string name="assistant_screen_task_delete_fail_message">Úloha úspěšně smazána</string>
<string name="assistant_screen_unknown_task_status_text">Neznámé</string>
<string name="associated_account_not_found">Související účet nenalezen!</string> <string name="associated_account_not_found">Související účet nenalezen!</string>
<string name="auth_access_failed">Přístup se nezdařil: %1$s</string> <string name="auth_access_failed">Přístup se nezdařil: %1$s</string>
<string name="auth_account_does_not_exist">Účet zatím není na tomto zařízení přidán</string> <string name="auth_account_does_not_exist">Účet zatím není na tomto zařízení přidán</string>

View file

@ -37,6 +37,9 @@
<string name="app_widget_description">Viser én widget fra dashboard</string> <string name="app_widget_description">Viser én widget fra dashboard</string>
<string name="appbar_search_in">Søg i %s</string> <string name="appbar_search_in">Søg i %s</string>
<string name="assistant_screen_all_task_type">Alle</string> <string name="assistant_screen_all_task_type">Alle</string>
<string name="assistant_screen_scheduled_task_status_text">Planlagt</string>
<string name="assistant_screen_successful_task_text">Fuldført</string>
<string name="assistant_screen_unknown_task_status_text">Ukendt</string>
<string name="associated_account_not_found">Forbundet konto blev ikke fundet!</string> <string name="associated_account_not_found">Forbundet konto blev ikke fundet!</string>
<string name="auth_access_failed">Adgang fejlede: %1$s</string> <string name="auth_access_failed">Adgang fejlede: %1$s</string>
<string name="auth_account_does_not_exist">Kontoen findes endnu ikke på enheden</string> <string name="auth_account_does_not_exist">Kontoen findes endnu ikke på enheden</string>

View file

@ -45,6 +45,9 @@
<string name="assistant_screen_loading">Aufgabenlisten werden geladen, bitte warten</string> <string name="assistant_screen_loading">Aufgabenlisten werden geladen, bitte warten</string>
<string name="assistant_screen_no_task_available_for_all_task_filter_text">Keine Aufgabe verfügbar. Aufgabentyp auswählen, um eine neue Aufgabe zu erstellen.</string> <string name="assistant_screen_no_task_available_for_all_task_filter_text">Keine Aufgabe verfügbar. Aufgabentyp auswählen, um eine neue Aufgabe zu erstellen.</string>
<string name="assistant_screen_no_task_available_text">Für den Aufgabentyp %s ist keine Aufgabe verfügbar. Sie können unten rechts eine neue Aufgabe erstellen.</string> <string name="assistant_screen_no_task_available_text">Für den Aufgabentyp %s ist keine Aufgabe verfügbar. Sie können unten rechts eine neue Aufgabe erstellen.</string>
<string name="assistant_screen_running_task_text">In Bearbeitung</string>
<string name="assistant_screen_scheduled_task_status_text">Geplant</string>
<string name="assistant_screen_successful_task_text">Fertiggestellt</string>
<string name="assistant_screen_task_create_fail_message">Es ist ein Fehler beim Erstellen der Aufgabe aufgetreten</string> <string name="assistant_screen_task_create_fail_message">Es ist ein Fehler beim Erstellen der Aufgabe aufgetreten</string>
<string name="assistant_screen_task_create_success_message">Aufgabe erfolgreich erstellt</string> <string name="assistant_screen_task_create_success_message">Aufgabe erfolgreich erstellt</string>
<string name="assistant_screen_task_delete_fail_message">Aufgabe erfolgreich gelöscht</string> <string name="assistant_screen_task_delete_fail_message">Aufgabe erfolgreich gelöscht</string>
@ -53,6 +56,7 @@
<string name="assistant_screen_task_more_actions_bottom_sheet_delete_action">Aufgabe löschen</string> <string name="assistant_screen_task_more_actions_bottom_sheet_delete_action">Aufgabe löschen</string>
<string name="assistant_screen_task_types_error_state_message">Die Aufgabentypen können nicht abgerufen werden. Bitte überprüfen Sie Ihre Internetverbindung.</string> <string name="assistant_screen_task_types_error_state_message">Die Aufgabentypen können nicht abgerufen werden. Bitte überprüfen Sie Ihre Internetverbindung.</string>
<string name="assistant_screen_top_bar_title">Assistent</string> <string name="assistant_screen_top_bar_title">Assistent</string>
<string name="assistant_screen_unknown_task_status_text">Unbekannt</string>
<string name="associated_account_not_found">Verknüpftes Konto nicht gefunden!</string> <string name="associated_account_not_found">Verknüpftes Konto nicht gefunden!</string>
<string name="auth_access_failed">Zugriffsfehler: %1$s</string> <string name="auth_access_failed">Zugriffsfehler: %1$s</string>
<string name="auth_account_does_not_exist">Das Konto ist bislang auf dem Gerät nicht vorhanden</string> <string name="auth_account_does_not_exist">Das Konto ist bislang auf dem Gerät nicht vorhanden</string>

View file

@ -41,9 +41,12 @@
<string name="assistant_screen_create_task_alert_dialog_input_field_placeholder">Escriba algo de texto</string> <string name="assistant_screen_create_task_alert_dialog_input_field_placeholder">Escriba algo de texto</string>
<string name="assistant_screen_delete_task_alert_dialog_description">¿Está seguro de eliminar esta tarea?</string> <string name="assistant_screen_delete_task_alert_dialog_description">¿Está seguro de eliminar esta tarea?</string>
<string name="assistant_screen_delete_task_alert_dialog_title">Eliminar tarea</string> <string name="assistant_screen_delete_task_alert_dialog_title">Eliminar tarea</string>
<string name="assistant_screen_failed_task_text">Falló</string>
<string name="assistant_screen_loading">Las listas de tareas están cargando, por favor espere</string> <string name="assistant_screen_loading">Las listas de tareas están cargando, por favor espere</string>
<string name="assistant_screen_no_task_available_for_all_task_filter_text">No hay tareas disponibles. Seleccione un tipo de tarea para crear una nueva.</string> <string name="assistant_screen_no_task_available_for_all_task_filter_text">No hay tareas disponibles. Seleccione un tipo de tarea para crear una nueva.</string>
<string name="assistant_screen_no_task_available_text">No hay tareas disponibles para el tipo de tarea %s, puede crear una nueva tarea abajo a la derecha.</string> <string name="assistant_screen_no_task_available_text">No hay tareas disponibles para el tipo de tarea %s, puede crear una nueva tarea abajo a la derecha.</string>
<string name="assistant_screen_running_task_text">En proceso</string>
<string name="assistant_screen_scheduled_task_status_text">Programado</string>
<string name="assistant_screen_successful_task_text">Completado</string> <string name="assistant_screen_successful_task_text">Completado</string>
<string name="assistant_screen_task_create_fail_message">Ocurrió un error al crear la tarea</string> <string name="assistant_screen_task_create_fail_message">Ocurrió un error al crear la tarea</string>
<string name="assistant_screen_task_create_success_message">Tarea creada exitosamente</string> <string name="assistant_screen_task_create_success_message">Tarea creada exitosamente</string>

View file

@ -45,13 +45,16 @@
<string name="assistant_screen_loading">Task List are loading, please wait</string> <string name="assistant_screen_loading">Task List are loading, please wait</string>
<string name="assistant_screen_no_task_available_for_all_task_filter_text">No task available. Select a task type to create a new task.</string> <string name="assistant_screen_no_task_available_for_all_task_filter_text">No task available. Select a task type to create a new task.</string>
<string name="assistant_screen_no_task_available_text">No task available for %s task type, you can create a new task from bottom right.</string> <string name="assistant_screen_no_task_available_text">No task available for %s task type, you can create a new task from bottom right.</string>
<string name="assistant_screen_running_task_text">En cours</string>
<string name="assistant_screen_scheduled_task_status_text">Planifié</string> <string name="assistant_screen_scheduled_task_status_text">Planifié</string>
<string name="assistant_screen_successful_task_text">Terminé</string> <string name="assistant_screen_successful_task_text">Terminé</string>
<string name="assistant_screen_task_create_fail_message">An error occurred while creating the task</string> <string name="assistant_screen_task_create_fail_message">An error occurred while creating the task</string>
<string name="assistant_screen_task_create_success_message">Task successfully created</string> <string name="assistant_screen_task_create_success_message">Task successfully created</string>
<string name="assistant_screen_task_delete_fail_message">Task successfully deleted</string> <string name="assistant_screen_task_delete_fail_message">Task successfully deleted</string>
<string name="assistant_screen_task_delete_success_message">An error occurred while deleting the task</string> <string name="assistant_screen_task_delete_success_message">An error occurred while deleting the task</string>
<string name="assistant_screen_task_list_error_state_message">Impossible de récupérer la liste des tâches, veuillez vérifier votre connexion Internet.</string>
<string name="assistant_screen_task_more_actions_bottom_sheet_delete_action">Delete Task</string> <string name="assistant_screen_task_more_actions_bottom_sheet_delete_action">Delete Task</string>
<string name="assistant_screen_task_types_error_state_message">Impossible de récupérer les types des tâches, veuillez vérifier votre connexion Internet.</string>
<string name="assistant_screen_top_bar_title">Assistant</string> <string name="assistant_screen_top_bar_title">Assistant</string>
<string name="assistant_screen_unknown_task_status_text">Inconnu</string> <string name="assistant_screen_unknown_task_status_text">Inconnu</string>
<string name="associated_account_not_found">Compte associé introuvable !</string> <string name="associated_account_not_found">Compte associé introuvable !</string>

View file

@ -866,6 +866,7 @@
<string name="uploader_error_title_file_cannot_be_uploaded">このファイルはアップロードできません</string> <string name="uploader_error_title_file_cannot_be_uploaded">このファイルはアップロードできません</string>
<string name="uploader_error_title_no_file_to_upload">アップロードするファイルはありません</string> <string name="uploader_error_title_no_file_to_upload">アップロードするファイルはありません</string>
<string name="uploader_info_dirname">フォルダー名</string> <string name="uploader_info_dirname">フォルダー名</string>
<string name="uploader_local_files_uploaded">失敗したローカル ファイルのアップロードを再試行します</string>
<string name="uploader_top_message">アップロードフォルダーを選択</string> <string name="uploader_top_message">アップロードフォルダーを選択</string>
<string name="uploader_upload_failed_content_single">%1$sをアップロードできませんでした</string> <string name="uploader_upload_failed_content_single">%1$sをアップロードできませんでした</string>
<string name="uploader_upload_failed_credentials_error">アップロード失敗、要 再ログイン</string> <string name="uploader_upload_failed_credentials_error">アップロード失敗、要 再ログイン</string>

View file

@ -45,6 +45,7 @@
<string name="assistant_screen_loading">Oppgaveliste lastes inn, vennligst vent</string> <string name="assistant_screen_loading">Oppgaveliste lastes inn, vennligst vent</string>
<string name="assistant_screen_no_task_available_for_all_task_filter_text">Ingen oppgave tilgjengelig. Velg en oppgavetype for å opprette en ny oppgave.</string> <string name="assistant_screen_no_task_available_for_all_task_filter_text">Ingen oppgave tilgjengelig. Velg en oppgavetype for å opprette en ny oppgave.</string>
<string name="assistant_screen_no_task_available_text">Ingen oppgave tilgjengelig for %s-oppgavetype, du kan opprette en ny oppgave fra nede til høyre.</string> <string name="assistant_screen_no_task_available_text">Ingen oppgave tilgjengelig for %s-oppgavetype, du kan opprette en ny oppgave fra nede til høyre.</string>
<string name="assistant_screen_running_task_text">Pågår</string>
<string name="assistant_screen_scheduled_task_status_text">Planlagt</string> <string name="assistant_screen_scheduled_task_status_text">Planlagt</string>
<string name="assistant_screen_successful_task_text">Ferdig</string> <string name="assistant_screen_successful_task_text">Ferdig</string>
<string name="assistant_screen_task_create_fail_message">Det oppstod en feil under oppretting av oppgaven</string> <string name="assistant_screen_task_create_fail_message">Det oppstod en feil under oppretting av oppgaven</string>

View file

@ -45,13 +45,16 @@
<string name="assistant_screen_loading">Zoznam úloh sa nahráva, prosím čakajte</string> <string name="assistant_screen_loading">Zoznam úloh sa nahráva, prosím čakajte</string>
<string name="assistant_screen_no_task_available_for_all_task_filter_text">Žiadne úlohy nie su dostupné. Vyberte typ úlohy pre vytvorenie novej.</string> <string name="assistant_screen_no_task_available_for_all_task_filter_text">Žiadne úlohy nie su dostupné. Vyberte typ úlohy pre vytvorenie novej.</string>
<string name="assistant_screen_no_task_available_text">Pre typ úlohy %s nie je k dispozícii žiadna úloha, môžete vytvoriť novú úlohu vpravo dole.</string> <string name="assistant_screen_no_task_available_text">Pre typ úlohy %s nie je k dispozícii žiadna úloha, môžete vytvoriť novú úlohu vpravo dole.</string>
<string name="assistant_screen_running_task_text">Prebieha</string>
<string name="assistant_screen_scheduled_task_status_text">Naplánované</string> <string name="assistant_screen_scheduled_task_status_text">Naplánované</string>
<string name="assistant_screen_successful_task_text">Dokončené</string> <string name="assistant_screen_successful_task_text">Dokončené</string>
<string name="assistant_screen_task_create_fail_message">Pri vytváraní úlohy nastala chyba</string> <string name="assistant_screen_task_create_fail_message">Pri vytváraní úlohy nastala chyba</string>
<string name="assistant_screen_task_create_success_message">Úloha bola úspešne vytvorená</string> <string name="assistant_screen_task_create_success_message">Úloha bola úspešne vytvorená</string>
<string name="assistant_screen_task_delete_fail_message">Úloha bola úspešne odstránená</string> <string name="assistant_screen_task_delete_fail_message">Úloha bola úspešne odstránená</string>
<string name="assistant_screen_task_delete_success_message">Pri odstraňovaní úlohy nastala chyba</string> <string name="assistant_screen_task_delete_success_message">Pri odstraňovaní úlohy nastala chyba</string>
<string name="assistant_screen_task_list_error_state_message">Nie je možné načítať zoznam úloh, skontrolujte svoje internetové pripojenie.</string>
<string name="assistant_screen_task_more_actions_bottom_sheet_delete_action">Vymazať Úlohu</string> <string name="assistant_screen_task_more_actions_bottom_sheet_delete_action">Vymazať Úlohu</string>
<string name="assistant_screen_task_types_error_state_message">Nie je možné načítať typy úloh, skontrolujte svoje internetové pripojenie.</string>
<string name="assistant_screen_top_bar_title">Asistent</string> <string name="assistant_screen_top_bar_title">Asistent</string>
<string name="assistant_screen_unknown_task_status_text">Neznámy</string> <string name="assistant_screen_unknown_task_status_text">Neznámy</string>
<string name="associated_account_not_found">Priradený účet sa nenašiel</string> <string name="associated_account_not_found">Priradený účet sa nenašiel</string>

View file

@ -45,6 +45,7 @@
<string name="assistant_screen_loading">Листа задатака се учитава, молимо вас сачекајте</string> <string name="assistant_screen_loading">Листа задатака се учитава, молимо вас сачекајте</string>
<string name="assistant_screen_no_task_available_for_all_task_filter_text">Не постоји ниједан задатак. Да бисте креирали нови задатак, изаберите тип задатка.</string> <string name="assistant_screen_no_task_available_for_all_task_filter_text">Не постоји ниједан задатак. Да бисте креирали нови задатак, изаберите тип задатка.</string>
<string name="assistant_screen_no_task_available_text">Не постоји ниједан задатак типа %s, нови задатак можете да креирате доле десно.</string> <string name="assistant_screen_no_task_available_text">Не постоји ниједан задатак типа %s, нови задатак можете да креирате доле десно.</string>
<string name="assistant_screen_running_task_text">Напредује</string>
<string name="assistant_screen_scheduled_task_status_text">Заказано</string> <string name="assistant_screen_scheduled_task_status_text">Заказано</string>
<string name="assistant_screen_successful_task_text">Завршено</string> <string name="assistant_screen_successful_task_text">Завршено</string>
<string name="assistant_screen_task_create_fail_message">Дошло је до грешке током креирања задатка</string> <string name="assistant_screen_task_create_fail_message">Дошло је до грешке током креирања задатка</string>

View file

@ -41,9 +41,12 @@
<string name="assistant_screen_create_task_alert_dialog_input_field_placeholder">Skriv någon text</string> <string name="assistant_screen_create_task_alert_dialog_input_field_placeholder">Skriv någon text</string>
<string name="assistant_screen_delete_task_alert_dialog_description">Är du säker på att du vill ta bort den här uppgiften?</string> <string name="assistant_screen_delete_task_alert_dialog_description">Är du säker på att du vill ta bort den här uppgiften?</string>
<string name="assistant_screen_delete_task_alert_dialog_title">Ta bort uppgift</string> <string name="assistant_screen_delete_task_alert_dialog_title">Ta bort uppgift</string>
<string name="assistant_screen_failed_task_text">Misslyckades</string>
<string name="assistant_screen_loading">Uppgiftslistan laddas, vänta</string> <string name="assistant_screen_loading">Uppgiftslistan laddas, vänta</string>
<string name="assistant_screen_no_task_available_for_all_task_filter_text">Ingen uppgift tillgänglig. Välj en uppgiftstyp för att skapa en ny uppgift.</string> <string name="assistant_screen_no_task_available_for_all_task_filter_text">Ingen uppgift tillgänglig. Välj en uppgiftstyp för att skapa en ny uppgift.</string>
<string name="assistant_screen_no_task_available_text">Ingen uppgift tillgänglig för uppgiftstyp %s, du kan skapa en ny uppgift längst ner till höger.</string> <string name="assistant_screen_no_task_available_text">Ingen uppgift tillgänglig för uppgiftstyp %s, du kan skapa en ny uppgift längst ner till höger.</string>
<string name="assistant_screen_running_task_text">Pågående</string>
<string name="assistant_screen_scheduled_task_status_text">Schemalagd</string>
<string name="assistant_screen_successful_task_text">Slutförd</string> <string name="assistant_screen_successful_task_text">Slutförd</string>
<string name="assistant_screen_task_create_fail_message">Ett fel uppstod när uppgiften skapades</string> <string name="assistant_screen_task_create_fail_message">Ett fel uppstod när uppgiften skapades</string>
<string name="assistant_screen_task_create_success_message">Uppgiften har skapats</string> <string name="assistant_screen_task_create_success_message">Uppgiften har skapats</string>