diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 000000000000..4262db412756 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,4 @@ +include: + - project: ${CI_PROJECT_PATH} + ref: ci_config + file: '.gitlab-ci.yml' diff --git a/.idea/copyright/IONOS_HiDrive_Next.xml b/.idea/copyright/IONOS_HiDrive_Next.xml new file mode 100644 index 000000000000..d313733e68e6 --- /dev/null +++ b/.idea/copyright/IONOS_HiDrive_Next.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml index bc106e52608a..2e31899f7ffd 100644 --- a/.idea/copyright/profiles_settings.xml +++ b/.idea/copyright/profiles_settings.xml @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index b3eec615802e..c957d3a814e9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -24,6 +24,8 @@ buildscript { classpath "org.jacoco:org.jacoco.core:$jacoco_version" classpath "org.jacoco:org.jacoco.report:$jacoco_version" classpath "org.jacoco:org.jacoco.agent:$jacoco_version" + classpath 'com.google.gms:google-services:4.4.2' + classpath "com.google.firebase:firebase-crashlytics-gradle:3.0.2" } } @@ -43,6 +45,7 @@ apply plugin: 'pmd' apply from: "$rootProject.projectDir/jacoco.gradle" apply plugin: 'com.github.spotbugs' apply plugin: 'io.gitlab.arturbosch.detekt' +apply plugin: 'com.google.firebase.crashlytics' // needed to make renovate run without shot, as shot requires Android SDK // https://github.com/pedrovgs/Shot/issues/300 @@ -51,6 +54,9 @@ if (shotTest) { } apply plugin: 'com.google.devtools.ksp' +if (getGradle().getStartParameter().getTaskRequests().toString().contains("Gplay")){ + apply plugin: 'com.google.gms.google-services' +} println "Gradle uses Java ${Jvm.current()}" @@ -162,8 +168,11 @@ android { } gplay { - applicationId 'com.nextcloud.client' + applicationId "com.ionos.hidrivenext" dimension "default" + versionCode 2 + isDefault = true + resConfigs "en", "de", "es", "fr", "nl" } huawei { @@ -321,6 +330,7 @@ dependencies { gplayImplementation project(':appscan') huaweiImplementation project(':appscan') qaImplementation project(':appscan') + implementation project(':scanbot') spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.13.0' spotbugsPlugins 'com.mebigfatguy.fb-contrib:fb-contrib:7.6.4' @@ -428,6 +438,10 @@ dependencies { // splash screen dependency ref: https://developer.android.com/develop/ui/views/launch/splash-screen/migrate implementation 'androidx.core:core-splashscreen:1.0.1' + + implementation(platform("com.google.firebase:firebase-bom:33.7.0")) + implementation "com.google.firebase:firebase-analytics" + implementation "com.google.firebase:firebase-crashlytics" } configurations.configureEach { diff --git a/app/src/gplay/AndroidManifest.xml b/app/src/gplay/AndroidManifest.xml index 9e2d78552bcf..38b688ee5d61 100644 --- a/app/src/gplay/AndroidManifest.xml +++ b/app/src/gplay/AndroidManifest.xml @@ -1,73 +1,51 @@ - - + ~ SPDX-FileCopyrightText: 2025 STRATO GmbH. + ~ SPDX-License-Identifier: GPL-2.0 + --> + xmlns:tools="http://schemas.android.com/tools"> - + - - - - - - + android:name="firebase_analytics_collection_enabled" + android:value="false" + tools:node="merge" /> + + - - + android:name=".ui.preview.PreviewImageActivity" + android:theme="@style/Theme.ownCloud.PreviewImage" + tools:replace="android:theme" /> - - - - - - - - - + - + + - - - - - + tools:node="merge"> + + - + \ No newline at end of file diff --git a/app/src/gplay/google-services.json b/app/src/gplay/debug/google-services.json similarity index 50% rename from app/src/gplay/google-services.json rename to app/src/gplay/debug/google-services.json index 491612656164..278d4a9f0a0c 100644 --- a/app/src/gplay/google-services.json +++ b/app/src/gplay/debug/google-services.json @@ -1,35 +1,29 @@ { "project_info": { - "project_number": "", - "project_id": "" + "project_number": "695238986129", + "project_id": "ionos-easystorage-dev", + "storage_bucket": "ionos-easystorage-dev.firebasestorage.app" }, "client": [ { "client_info": { - "mobilesdk_app_id": "", + "mobilesdk_app_id": "1:695238986129:android:21f017261e3d0f7bc0f230", "android_client_info": { - "package_name": "com.nextcloud.client" + "package_name": "com.ionos.hidrivenext" } }, "oauth_client": [], "api_key": [ { - "current_key": "" + "current_key": "AIzaSyDgq5tBI0SVPspdtVrRx-z-fBR9XsbZeUw" } ], "services": { - "analytics_service": { - "status": 1 - }, "appinvite_service": { - "status": 1, "other_platform_oauth_client": [] - }, - "ads_service": { - "status": 1 } } } ], "configuration_version": "1" -} +} \ No newline at end of file diff --git a/app/src/gplay/ic_launcher-playstore.png b/app/src/gplay/ic_launcher-playstore.png new file mode 100644 index 000000000000..2d58e972b106 Binary files /dev/null and b/app/src/gplay/ic_launcher-playstore.png differ diff --git a/app/src/gplay/java/com/nextcloud/android/appReview/InAppReviewHelperImpl.kt b/app/src/gplay/java/com/nextcloud/android/appReview/InAppReviewHelperImpl.kt index 01208c9a7a6c..23fc4178af07 100644 --- a/app/src/gplay/java/com/nextcloud/android/appReview/InAppReviewHelperImpl.kt +++ b/app/src/gplay/java/com/nextcloud/android/appReview/InAppReviewHelperImpl.kt @@ -3,117 +3,20 @@ * * SPDX-FileCopyrightText: 2023 Tobias Kaminsky * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: AGPL-3.0-or-later */ package com.nextcloud.android.appReview import androidx.appcompat.app.AppCompatActivity -import com.google.android.gms.tasks.Task -import com.google.android.play.core.review.ReviewInfo -import com.google.android.play.core.review.ReviewManager -import com.google.android.play.core.review.ReviewManagerFactory -import com.nextcloud.appReview.AppReviewShownModel import com.nextcloud.appReview.InAppReviewHelper import com.nextcloud.client.preferences.AppPreferences -import com.nextcloud.utils.extensions.getFormattedStringDate -import com.nextcloud.utils.extensions.isCurrentYear -import com.owncloud.android.lib.common.utils.Log_OC - -// Reference: https://developer.android.com/guide/playcore/in-app-review -/** - * This class responsible to handle & manage in-app review related methods - */ -class InAppReviewHelperImpl(val appPreferences: AppPreferences) : InAppReviewHelper { +class InAppReviewHelperImpl(appPreferences: AppPreferences) : + InAppReviewHelper { override fun resetAndIncrementAppRestartCounter() { - val appReviewShownModel = appPreferences.inAppReviewData - val currentTimeMills = System.currentTimeMillis() - - if (appReviewShownModel != null) { - if (currentTimeMills.isCurrentYear(appReviewShownModel.firstShowYear)) { - appReviewShownModel.appRestartCount += 1 - appPreferences.setInAppReviewData(appReviewShownModel) - } else { - resetReviewShownModel() - } - } else { - resetReviewShownModel() - } - } - - private fun resetReviewShownModel() { - val appReviewShownModel = AppReviewShownModel( - System.currentTimeMillis().getFormattedStringDate(YEAR_FORMAT), - 1, - 0, - null - ) - appPreferences.setInAppReviewData(appReviewShownModel) } override fun showInAppReview(activity: AppCompatActivity) { - val appReviewShownModel = appPreferences.inAppReviewData - val currentTimeMills = System.currentTimeMillis() - - appReviewShownModel?.let { - if (it.appRestartCount >= MIN_APP_RESTARTS_REQ && - currentTimeMills.isCurrentYear(it.firstShowYear) && - it.reviewShownCount < MAX_DISPLAY_PER_YEAR - ) { - doAppReview(activity) - } else { - Log_OC.d( - TAG, - "Yearly limit has been reached or minimum app restarts are not completed: $appReviewShownModel" - ) - } - } - } - - private fun doAppReview(activity: AppCompatActivity) { - val manager = ReviewManagerFactory.create(activity) - val request: Task = manager.requestReviewFlow() - request.addOnCompleteListener { task -> - if (task.isSuccessful) { - // We can get the ReviewInfo object - val reviewInfo: ReviewInfo = task.result!! - launchAppReviewFlow(manager, activity, reviewInfo) - } else { - // There was some problem, log or handle the error code. - Log_OC.e(TAG, "Failed to get ReviewInfo: ${task.exception?.message}") - } - } - } - - private fun launchAppReviewFlow(manager: ReviewManager, activity: AppCompatActivity, reviewInfo: ReviewInfo) { - val flow = manager.launchReviewFlow(activity, reviewInfo) - flow.addOnCompleteListener { _ -> - // The flow has finished. The API does not indicate whether the user - // reviewed or not, or even whether the review dialog was shown. Thus, no - // matter the result, we continue our app flow. - // Scenarios in which the flow won't shown: - // 1. Showing dialog to frequently - // 2. If quota is reached can be checked in official documentation - // 3. Flow won't be shown if user has already reviewed the app. User has to delete the review from play store to show the review dialog again - // Link for more info: https://stackoverflow.com/a/63342266 - Log_OC.d(TAG, "App Review flow is completed") - } - - // on successful showing review dialog increment the count and capture the date - val appReviewShownModel = appPreferences.inAppReviewData - appReviewShownModel?.let { - it.appRestartCount = 0 - it.reviewShownCount += 1 - it.lastReviewShownDate = System.currentTimeMillis().getFormattedStringDate(DATE_TIME_FORMAT) - appPreferences.setInAppReviewData(it) - } - } - - companion object { - private val TAG = InAppReviewHelperImpl::class.java.simpleName - const val YEAR_FORMAT = "yyyy" - const val DATE_TIME_FORMAT = "dd-MM-yyyy HH:mm:ss" - const val MIN_APP_RESTARTS_REQ = 10 // minimum app restarts required to ask the review - const val MAX_DISPLAY_PER_YEAR = 15 // maximum times to ask review in a year } } diff --git a/app/src/gplay/java/com/nextcloud/client/di/VariantComponentsModule.java b/app/src/gplay/java/com/nextcloud/client/di/VariantComponentsModule.java index 6c807b6392b7..cd424d265857 100644 --- a/app/src/gplay/java/com/nextcloud/client/di/VariantComponentsModule.java +++ b/app/src/gplay/java/com/nextcloud/client/di/VariantComponentsModule.java @@ -2,21 +2,13 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: AGPL-3.0-or-later */ package com.nextcloud.client.di; -import com.owncloud.android.authentication.ModifiedAuthenticatorActivity; -import com.owncloud.android.services.firebase.NCFirebaseMessagingService; - import dagger.Module; -import dagger.android.ContributesAndroidInjector; @Module abstract class VariantComponentsModule { - @ContributesAndroidInjector - abstract NCFirebaseMessagingService nCFirebaseMessagingService(); - - @ContributesAndroidInjector - abstract ModifiedAuthenticatorActivity modifiedAuthenticatorActivity(); } diff --git a/app/src/gplay/java/com/nextcloud/client/di/VariantModule.kt b/app/src/gplay/java/com/nextcloud/client/di/VariantModule.kt index b909b6285c16..5846fcddf22e 100644 --- a/app/src/gplay/java/com/nextcloud/client/di/VariantModule.kt +++ b/app/src/gplay/java/com/nextcloud/client/di/VariantModule.kt @@ -3,10 +3,14 @@ * * SPDX-FileCopyrightText: 2023 Álvaro Brey * SPDX-FileCopyrightText: 2023 Nextcloud GmbH - * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: AGPL-3.0-or-later */ package com.nextcloud.client.di +import com.ionos.scanbot.availability.Availability +import com.ionos.scanbot.di.qualifiers.Scanbot +import com.ionos.scanbot.di.qualifiers.ScanbotLicense import com.nextcloud.appscan.ScanPageContract import com.nextcloud.client.documentscan.AppScanOptionalFeature import dagger.Module @@ -17,9 +21,13 @@ import dagger.Reusable internal class VariantModule { @Provides @Reusable - fun scanOptionalFeature(): AppScanOptionalFeature { + fun scanOptionalFeature( + @Scanbot featureAvailability: Availability, + @ScanbotLicense licenseAvailability: Availability + ): AppScanOptionalFeature { return object : AppScanOptionalFeature() { override fun getScanContract() = ScanPageContract() + override val isAvailable: Boolean = featureAvailability.available() && licenseAvailability.available() } } } diff --git a/app/src/gplay/java/com/owncloud/android/authentication/ModifiedAuthenticatorActivity.java b/app/src/gplay/java/com/owncloud/android/authentication/ModifiedAuthenticatorActivity.java deleted file mode 100644 index 81e03ec834fd..000000000000 --- a/app/src/gplay/java/com/owncloud/android/authentication/ModifiedAuthenticatorActivity.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2017 Mario Danic - * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only - */ -package com.owncloud.android.authentication; - -import android.os.Bundle; - -import com.nextcloud.client.di.Injectable; -import com.owncloud.android.utils.GooglePlayUtils; - -public class ModifiedAuthenticatorActivity extends AuthenticatorActivity implements Injectable { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - GooglePlayUtils.checkPlayServices(this); - } - - @Override - protected void onResume() { - super.onResume(); - GooglePlayUtils.checkPlayServices(this); - } - -} diff --git a/app/src/gplay/java/com/owncloud/android/services/firebase/NCFirebaseMessagingService.java b/app/src/gplay/java/com/owncloud/android/services/firebase/NCFirebaseMessagingService.java deleted file mode 100644 index d1bc2712b762..000000000000 --- a/app/src/gplay/java/com/owncloud/android/services/firebase/NCFirebaseMessagingService.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2020 Chris Narkiewicz - * SPDX-FileCopyrightText: 2017 Mario Danic - * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only - */ -package com.owncloud.android.services.firebase; - -import android.text.TextUtils; - -import com.google.firebase.messaging.FirebaseMessagingService; -import com.google.firebase.messaging.RemoteMessage; -import com.nextcloud.client.account.UserAccountManager; -import com.nextcloud.client.jobs.BackgroundJobManager; -import com.nextcloud.client.jobs.NotificationWork; -import com.nextcloud.client.preferences.AppPreferences; -import com.owncloud.android.R; -import com.owncloud.android.utils.PushUtils; - -import java.util.Map; - -import javax.inject.Inject; - -import androidx.annotation.NonNull; -import dagger.android.AndroidInjection; - -public class NCFirebaseMessagingService extends FirebaseMessagingService { - @Inject AppPreferences preferences; - @Inject UserAccountManager accountManager; - @Inject BackgroundJobManager backgroundJobManager; - - @Override - public void onCreate() { - super.onCreate(); - AndroidInjection.inject(this); - } - - @Override - public void onMessageReceived(@NonNull RemoteMessage remoteMessage) { - final Map data = remoteMessage.getData(); - final String subject = data.get(NotificationWork.KEY_NOTIFICATION_SUBJECT); - final String signature = data.get(NotificationWork.KEY_NOTIFICATION_SIGNATURE); - if (subject != null && signature != null) { - backgroundJobManager.startNotificationJob(subject, signature); - } - } - - @Override - public void onNewToken(@NonNull String newToken) { - super.onNewToken(newToken); - - if (!TextUtils.isEmpty(getResources().getString(R.string.push_server_url))) { - preferences.setPushToken(newToken); - PushUtils.pushRegistrationToServer(accountManager, preferences.getPushToken()); - } - } -} diff --git a/app/src/gplay/java/com/owncloud/android/utils/GooglePlayUtils.kt b/app/src/gplay/java/com/owncloud/android/utils/GooglePlayUtils.kt deleted file mode 100644 index 9a307dddf609..000000000000 --- a/app/src/gplay/java/com/owncloud/android/utils/GooglePlayUtils.kt +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2017 Mario Danic - * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only - */ -package com.owncloud.android.utils - -import android.app.Activity -import com.google.android.gms.common.ConnectionResult -import com.google.android.gms.common.GoogleApiAvailability -import com.owncloud.android.lib.common.utils.Log_OC - -object GooglePlayUtils { - private const val PLAY_SERVICES_RESOLUTION_REQUEST = 9000 - private const val TAG = "GooglePlayUtils" - - @JvmStatic - fun checkPlayServices(activity: Activity): Boolean { - val apiAvailability = GoogleApiAvailability.getInstance() - val resultCode = apiAvailability.isGooglePlayServicesAvailable(activity) - if (resultCode != ConnectionResult.SUCCESS) { - if (apiAvailability.isUserResolvableError(resultCode)) { - apiAvailability.getErrorDialog(activity, resultCode, PLAY_SERVICES_RESOLUTION_REQUEST)?.show() - } else { - Log_OC.i(TAG, "This device is not supported.") - activity.finish() - } - return false - } - return true - } -} diff --git a/app/src/gplay/java/com/owncloud/android/utils/PushUtils.java b/app/src/gplay/java/com/owncloud/android/utils/PushUtils.java index 816612e6902a..e070e26849e1 100644 --- a/app/src/gplay/java/com/owncloud/android/utils/PushUtils.java +++ b/app/src/gplay/java/com/owncloud/android/utils/PushUtils.java @@ -1,454 +1,48 @@ /* * Nextcloud - Android Client * - * SPDX-FileCopyrightText: 2019 Tobias Kaminsky * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-FileCopyrightText: 2017-2018 Mario Danic - * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only + * SPDX-FileCopyrightText: 2017 Mario Danic + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: AGPL-3.0-or-later */ package com.owncloud.android.utils; -import android.accounts.Account; -import android.accounts.AuthenticatorException; -import android.accounts.OperationCanceledException; import android.content.Context; -import android.text.TextUtils; -import android.util.Base64; -import com.google.gson.Gson; import com.nextcloud.client.account.UserAccountManager; -import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.client.preferences.AppPreferencesImpl; -import com.nextcloud.common.NextcloudClient; import com.owncloud.android.MainApp; -import com.owncloud.android.R; -import com.owncloud.android.datamodel.ArbitraryDataProvider; -import com.owncloud.android.datamodel.ArbitraryDataProviderImpl; -import com.owncloud.android.datamodel.PushConfigurationState; import com.owncloud.android.datamodel.SignatureVerification; -import com.owncloud.android.lib.common.OwnCloudAccount; -import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.lib.resources.notifications.RegisterAccountDeviceForNotificationsOperation; -import com.owncloud.android.lib.resources.notifications.RegisterAccountDeviceForProxyOperation; -import com.owncloud.android.lib.resources.notifications.UnregisterAccountDeviceForNotificationsOperation; -import com.owncloud.android.lib.resources.notifications.UnregisterAccountDeviceForProxyOperation; -import com.owncloud.android.lib.resources.notifications.models.PushResponse; -import org.apache.commons.httpclient.HttpStatus; -import org.apache.commons.io.FileUtils; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.security.InvalidKeyException; import java.security.Key; -import java.security.KeyFactory; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; -import java.security.PublicKey; -import java.security.Signature; -import java.security.SignatureException; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.PKCS8EncodedKeySpec; -import java.security.spec.X509EncodedKeySpec; -import java.util.Locale; public final class PushUtils { - public static final String KEY_PUSH = "push"; - private static final String TAG = "PushUtils"; - private static final String KEYPAIR_FOLDER = "nc-keypair"; - private static final String KEYPAIR_FILE_NAME = "push_key"; - private static final String KEYPAIR_PRIV_EXTENSION = ".priv"; - private static final String KEYPAIR_PUB_EXTENSION = ".pub"; - private static ArbitraryDataProvider arbitraryDataProvider; private PushUtils() { } - public static String generateSHA512Hash(String pushToken) { - MessageDigest messageDigest = null; - try { - messageDigest = MessageDigest.getInstance("SHA-512"); - messageDigest.update(pushToken.getBytes()); - return EncryptionUtils.bytesToHex(messageDigest.digest()); - } catch (NoSuchAlgorithmException e) { - Log_OC.d(TAG, "SHA-512 algorithm not supported"); - } - return ""; - } - - private static int generateRsa2048KeyPair() { - migratePushKeys(); - String keyPath = MainApp.getAppContext().getFilesDir().getAbsolutePath() + File.separator + - MainApp.getDataFolder() + File.separator + KEYPAIR_FOLDER; - - String privateKeyPath = keyPath + File.separator + KEYPAIR_FILE_NAME + KEYPAIR_PRIV_EXTENSION; - String publicKeyPath = keyPath + File.separator + KEYPAIR_FILE_NAME + KEYPAIR_PUB_EXTENSION; - File keyPathFile = new File(keyPath); - - if (!new File(privateKeyPath).exists() && !new File(publicKeyPath).exists()) { - try { - if (!keyPathFile.exists()) { - keyPathFile.mkdir(); - } - KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); - keyGen.initialize(2048); - - KeyPair pair = keyGen.generateKeyPair(); - int statusPrivate = saveKeyToFile(pair.getPrivate(), privateKeyPath); - int statusPublic = saveKeyToFile(pair.getPublic(), publicKeyPath); - - if (statusPrivate == 0 && statusPublic == 0) { - // all went well - return 0; - } else { - return -2; - } - } catch (NoSuchAlgorithmException e) { - Log_OC.d(TAG, "RSA algorithm not supported"); - } - } else { - // we already have the key - return -1; - } - - // we failed to generate the key - return -2; + public static void pushRegistrationToServer(final UserAccountManager accountManager, final String pushToken) { + // do nothing } - private static void deleteRegistrationForAccount(Account account) { + public static void reinitKeys(UserAccountManager accountManager) { Context context = MainApp.getAppContext(); - OwnCloudAccount ocAccount; - arbitraryDataProvider = new ArbitraryDataProviderImpl(MainApp.getAppContext()); - - try { - ocAccount = new OwnCloudAccount(account, context); - NextcloudClient mClient = OwnCloudClientManagerFactory.getDefaultSingleton(). - getNextcloudClientFor(ocAccount, context); - - RemoteOperationResult remoteOperationResult = - new UnregisterAccountDeviceForNotificationsOperation().execute(mClient); - - if (remoteOperationResult.getHttpCode() == HttpStatus.SC_ACCEPTED) { - String arbitraryValue; - if (!TextUtils.isEmpty(arbitraryValue = arbitraryDataProvider.getValue(account.name, KEY_PUSH))) { - Gson gson = new Gson(); - PushConfigurationState pushArbitraryData = gson.fromJson(arbitraryValue, - PushConfigurationState.class); - RemoteOperationResult unregisterResult = new UnregisterAccountDeviceForProxyOperation( - context.getResources().getString(R.string.push_server_url), - pushArbitraryData.getDeviceIdentifier(), - pushArbitraryData.getDeviceIdentifierSignature(), - pushArbitraryData.getUserPublicKey()).run(); - - if (unregisterResult.isSuccess()) { - arbitraryDataProvider.deleteKeyForAccount(account.name, KEY_PUSH); - } - } - } - } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException e) { - Log_OC.d(TAG, "Failed to find an account"); - } catch (AuthenticatorException e) { - Log_OC.d(TAG, "Failed via AuthenticatorException"); - } catch (IOException e) { - Log_OC.d(TAG, "Failed via IOException"); - } catch (OperationCanceledException e) { - Log_OC.d(TAG, "Failed via OperationCanceledException"); - } - } - - public static void pushRegistrationToServer(final UserAccountManager accountManager, final String token) { - arbitraryDataProvider = new ArbitraryDataProviderImpl(MainApp.getAppContext()); - - if (!TextUtils.isEmpty(MainApp.getAppContext().getResources().getString(R.string.push_server_url)) && - !TextUtils.isEmpty(token)) { - PushUtils.generateRsa2048KeyPair(); - String pushTokenHash = PushUtils.generateSHA512Hash(token).toLowerCase(Locale.ROOT); - PublicKey devicePublicKey = (PublicKey) PushUtils.readKeyFromFile(true); - if (devicePublicKey != null) { - byte[] publicKeyBytes = Base64.encode(devicePublicKey.getEncoded(), Base64.NO_WRAP); - String publicKey = new String(publicKeyBytes); - publicKey = publicKey.replaceAll("(.{64})", "$1\n"); - - publicKey = "-----BEGIN PUBLIC KEY-----\n" + publicKey + "\n-----END PUBLIC KEY-----\n"; - - Context context = MainApp.getAppContext(); - String providerValue; - PushConfigurationState accountPushData; - Gson gson = new Gson(); - for (Account account : accountManager.getAccounts()) { - providerValue = arbitraryDataProvider.getValue(account.name, KEY_PUSH); - if (!TextUtils.isEmpty(providerValue)) { - accountPushData = gson.fromJson(providerValue, - PushConfigurationState.class); - } else { - accountPushData = null; - } - - if (accountPushData != null && !accountPushData.getPushToken().equals(token) && - !accountPushData.isShouldBeDeleted() || - TextUtils.isEmpty(providerValue)) { - try { - OwnCloudAccount ocAccount = new OwnCloudAccount(account, context); - NextcloudClient client = OwnCloudClientManagerFactory.getDefaultSingleton(). - getNextcloudClientFor(ocAccount, context); - - RemoteOperationResult remoteOperationResult = - new RegisterAccountDeviceForNotificationsOperation(pushTokenHash, - publicKey, - context.getResources().getString(R.string.push_server_url)) - .execute(client); - - if (remoteOperationResult.isSuccess()) { - PushResponse pushResponse = remoteOperationResult.getResultData(); - - RemoteOperationResult resultProxy = new RegisterAccountDeviceForProxyOperation( - context.getResources().getString(R.string.push_server_url), - token, pushResponse.getDeviceIdentifier(), - pushResponse.getSignature(), - pushResponse.getPublicKey(), - MainApp.getUserAgent()) - .run(); - - if (resultProxy.isSuccess()) { - PushConfigurationState pushArbitraryData = new PushConfigurationState(token, - pushResponse.getDeviceIdentifier(), pushResponse.getSignature(), - pushResponse.getPublicKey(), false); - arbitraryDataProvider.storeOrUpdateKeyValue(account.name, KEY_PUSH, - gson.toJson(pushArbitraryData)); - } - } else if (remoteOperationResult.getCode() == - RemoteOperationResult.ResultCode.ACCOUNT_USES_STANDARD_PASSWORD) { - arbitraryDataProvider.storeOrUpdateKeyValue(account.name, - UserAccountManager.ACCOUNT_USES_STANDARD_PASSWORD, "true"); - } - } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException e) { - Log_OC.d(TAG, "Failed to find an account"); - } catch (AuthenticatorException e) { - Log_OC.d(TAG, "Failed via AuthenticatorException"); - } catch (IOException e) { - Log_OC.d(TAG, "Failed via IOException"); - } catch (OperationCanceledException e) { - Log_OC.d(TAG, "Failed via OperationCanceledException"); - } - } else if (accountPushData != null && accountPushData.isShouldBeDeleted()) { - deleteRegistrationForAccount(account); - } - } - } - } + AppPreferencesImpl.fromContext(context).setKeysReInitEnabled(); } public static Key readKeyFromFile(boolean readPublicKey) { - String keyPath = MainApp.getAppContext().getFilesDir().getAbsolutePath() + File.separator + - MainApp.getDataFolder() + File.separator + KEYPAIR_FOLDER; - - String privateKeyPath = keyPath + File.separator + KEYPAIR_FILE_NAME + KEYPAIR_PRIV_EXTENSION; - String publicKeyPath = keyPath + File.separator + KEYPAIR_FILE_NAME + KEYPAIR_PUB_EXTENSION; - - String path; - - if (readPublicKey) { - path = publicKeyPath; - } else { - path = privateKeyPath; - } - - FileInputStream fileInputStream = null; - try { - fileInputStream = new FileInputStream(path); - byte[] bytes = new byte[fileInputStream.available()]; - fileInputStream.read(bytes); - - KeyFactory keyFactory = KeyFactory.getInstance("RSA"); - - if (readPublicKey) { - X509EncodedKeySpec keySpec = new X509EncodedKeySpec(bytes); - return keyFactory.generatePublic(keySpec); - } else { - PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes); - return keyFactory.generatePrivate(keySpec); - } - - } catch (FileNotFoundException e) { - Log_OC.d(TAG, "Failed to find path while reading the Key"); - } catch (IOException e) { - Log_OC.d(TAG, "IOException while reading the key"); - } catch (InvalidKeySpecException e) { - Log_OC.d(TAG, "InvalidKeySpecException while reading the key"); - } catch (NoSuchAlgorithmException e) { - Log_OC.d(TAG, "RSA algorithm not supported"); - } finally { - if (fileInputStream != null) { - try { - fileInputStream.close(); - } catch (IOException e) { - Log_OC.e(TAG, "Error closing input stream during reading key from file", e); - } - } - } - return null; } - private static int saveKeyToFile(Key key, String path) { - byte[] encoded = key.getEncoded(); - FileOutputStream keyFileOutputStream = null; - try { - if (!new File(path).exists()) { - File newFile = new File(path); - newFile.getParentFile().mkdirs(); - newFile.createNewFile(); - } - keyFileOutputStream = new FileOutputStream(path); - keyFileOutputStream.write(encoded); - return 0; - } catch (FileNotFoundException e) { - Log_OC.d(TAG, "Failed to save key to file"); - } catch (IOException e) { - Log_OC.d(TAG, "Failed to save key to file via IOException"); - } finally { - if (keyFileOutputStream != null) { - try { - keyFileOutputStream.close(); - } catch (IOException e) { - Log_OC.e(TAG, "Error closing input stream during reading key from file", e); - } - } - } - - return -1; - } - - public static void reinitKeys(final UserAccountManager accountManager) { - Context context = MainApp.getAppContext(); - Account[] accounts = accountManager.getAccounts(); - for (Account account : accounts) { - deleteRegistrationForAccount(account); - } - - String keyPath = context.getDir("nc-keypair", Context.MODE_PRIVATE).getAbsolutePath(); - File privateKeyFile = new File(keyPath, "push_key.priv"); - File publicKeyFile = new File(keyPath, "push_key.pub"); - - FileUtils.deleteQuietly(privateKeyFile); - FileUtils.deleteQuietly(publicKeyFile); - - AppPreferences preferences = AppPreferencesImpl.fromContext(context); - String pushToken = preferences.getPushToken(); - pushRegistrationToServer(accountManager, pushToken); - preferences.setKeysReInitEnabled(); - } - - private static void migratePushKeys() { - Context context = MainApp.getAppContext(); - AppPreferences preferences = AppPreferencesImpl.fromContext(context); - if (!preferences.isKeysMigrationEnabled()) { - String oldKeyPath = MainApp.getStoragePath() + File.separator + MainApp.getDataFolder() - + File.separator + "nc-keypair"; - File oldPrivateKeyFile = new File(oldKeyPath, "push_key.priv"); - File oldPublicKeyFile = new File(oldKeyPath, "push_key.pub"); - - String keyPath = context.getDir("nc-keypair", Context.MODE_PRIVATE).getAbsolutePath(); - File privateKeyFile = new File(keyPath, "push_key.priv"); - File publicKeyFile = new File(keyPath, "push_key.pub"); - - if ((privateKeyFile.exists() && publicKeyFile.exists()) || - (!oldPrivateKeyFile.exists() && !oldPublicKeyFile.exists())) { - preferences.setKeysMigrationEnabled(true); - } else { - if (oldPrivateKeyFile.exists()) { - FileStorageUtils.moveFile(oldPrivateKeyFile, privateKeyFile); - } - - if (oldPublicKeyFile.exists()) { - FileStorageUtils.moveFile(oldPublicKeyFile, publicKeyFile); - } - - if (privateKeyFile.exists() && publicKeyFile.exists()) { - preferences.setKeysMigrationEnabled(true); - } - } - } - } - public static SignatureVerification verifySignature( final Context context, final UserAccountManager accountManager, final byte[] signatureBytes, final byte[] subjectBytes ) { - Signature signature; - PublicKey publicKey; - - Account[] accounts = accountManager.getAccounts(); - - ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(context); - String arbitraryValue; - Gson gson = new Gson(); - PushConfigurationState pushArbitraryData; - - try { - signature = Signature.getInstance("SHA512withRSA"); - if (accounts.length > 0) { - for (Account account : accounts) { - if (!TextUtils.isEmpty(arbitraryValue = arbitraryDataProvider.getValue(account.name, KEY_PUSH))) { - pushArbitraryData = gson.fromJson(arbitraryValue, PushConfigurationState.class); - if (!pushArbitraryData.isShouldBeDeleted()) { - publicKey = (PublicKey) readKeyFromString(true, pushArbitraryData.getUserPublicKey()); - signature.initVerify(publicKey); - signature.update(subjectBytes); - if (signature.verify(signatureBytes)) { - return new SignatureVerification(true, account); - } - } - } - } - } - } catch (NoSuchAlgorithmException e) { - Log_OC.d(TAG, "No such algorithm"); - } catch (InvalidKeyException e) { - Log_OC.d(TAG, "Invalid key while trying to verify"); - } catch (SignatureException e) { - Log_OC.d(TAG, "Signature exception while trying to verify"); - } - - return new SignatureVerification(false, null); - } - - private static Key readKeyFromString(boolean readPublicKey, String keyString) { - String modifiedKey; - if (readPublicKey) { - modifiedKey = keyString.replaceAll("\\n", "").replace("-----BEGIN PUBLIC KEY-----", - "").replace("-----END PUBLIC KEY-----", ""); - } else { - modifiedKey = keyString.replaceAll("\\n", "").replace("-----BEGIN PRIVATE KEY-----", - "").replace("-----END PRIVATE KEY-----", ""); - } - - KeyFactory keyFactory; - try { - keyFactory = KeyFactory.getInstance("RSA"); - if (readPublicKey) { - X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.decode(modifiedKey, Base64.DEFAULT)); - return keyFactory.generatePublic(keySpec); - } else { - PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.decode(modifiedKey, Base64.DEFAULT)); - return keyFactory.generatePrivate(keySpec); - } - } catch (NoSuchAlgorithmException e) { - Log_OC.d("TAG", "No such algorithm while reading key from string"); - } catch (InvalidKeySpecException e) { - Log_OC.d("TAG", "Invalid key spec while reading key from string"); - } - return null; } + } diff --git a/app/src/gplay/java/com/owncloud/android/utils/SecurityUtils.java b/app/src/gplay/java/com/owncloud/android/utils/SecurityUtils.java index 0ee14db82385..5a98967aa4f4 100644 --- a/app/src/gplay/java/com/owncloud/android/utils/SecurityUtils.java +++ b/app/src/gplay/java/com/owncloud/android/utils/SecurityUtils.java @@ -2,27 +2,10 @@ * Nextcloud - Android Client * * SPDX-FileCopyrightText: 2018 Mario Danic - * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: AGPL-3.0-or-later */ package com.owncloud.android.utils; -import android.content.Intent; - -import com.google.android.gms.security.ProviderInstaller; -import com.owncloud.android.MainApp; - -public class SecurityUtils implements ProviderInstaller.ProviderInstallListener { - public SecurityUtils() { - ProviderInstaller.installIfNeededAsync(MainApp.getAppContext(), this); - } - - @Override - public void onProviderInstalled() { - // Does nothing - } - - @Override - public void onProviderInstallFailed(int i, Intent intent) { - // Does nothing - } +public class SecurityUtils { } diff --git a/app/src/gplay/release/google-services.json b/app/src/gplay/release/google-services.json new file mode 100644 index 000000000000..de9aa3e71aa1 --- /dev/null +++ b/app/src/gplay/release/google-services.json @@ -0,0 +1,29 @@ +{ + "project_info": { + "project_number": "816451688882", + "project_id": "ionos-hidrive-next-ios-aos", + "storage_bucket": "ionos-hidrive-next-ios-aos.firebasestorage.app" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:816451688882:android:7dad53bbb59c8413d12cab", + "android_client_info": { + "package_name": "com.ionos.hidrivenext" + } + }, + "oauth_client": [], + "api_key": [ + { + "current_key": "AIzaSyBBsP5BOzO1kwK1B-xa_pDGWU6je_WVBXQ" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/app/src/gplay/res/animator/progress_bar_login_indeterminate.xml b/app/src/gplay/res/animator/progress_bar_login_indeterminate.xml new file mode 100644 index 000000000000..fd660c547af9 --- /dev/null +++ b/app/src/gplay/res/animator/progress_bar_login_indeterminate.xml @@ -0,0 +1,16 @@ + + + + diff --git a/app/src/gplay/res/color-night/drawer_item_shape_color.xml b/app/src/gplay/res/color-night/drawer_item_shape_color.xml new file mode 100644 index 000000000000..a4d31dfa8a72 --- /dev/null +++ b/app/src/gplay/res/color-night/drawer_item_shape_color.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/app/src/gplay/res/color/drawer_item_shape_color.xml b/app/src/gplay/res/color/drawer_item_shape_color.xml new file mode 100644 index 000000000000..a4d31dfa8a72 --- /dev/null +++ b/app/src/gplay/res/color/drawer_item_shape_color.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/app/src/gplay/res/color/filled_button_bg_color.xml b/app/src/gplay/res/color/filled_button_bg_color.xml new file mode 100644 index 000000000000..518c975d874c --- /dev/null +++ b/app/src/gplay/res/color/filled_button_bg_color.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/app/src/gplay/res/color/filled_button_text_color.xml b/app/src/gplay/res/color/filled_button_text_color.xml new file mode 100644 index 000000000000..6eef0fe45d18 --- /dev/null +++ b/app/src/gplay/res/color/filled_button_text_color.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/app/src/gplay/res/color/outlined_button_bg_color.xml b/app/src/gplay/res/color/outlined_button_bg_color.xml new file mode 100644 index 000000000000..3f66ee2be899 --- /dev/null +++ b/app/src/gplay/res/color/outlined_button_bg_color.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/app/src/gplay/res/color/outlined_button_stroke_color.xml b/app/src/gplay/res/color/outlined_button_stroke_color.xml new file mode 100644 index 000000000000..0c25161bc46c --- /dev/null +++ b/app/src/gplay/res/color/outlined_button_stroke_color.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/app/src/gplay/res/color/outlined_button_text_color.xml b/app/src/gplay/res/color/outlined_button_text_color.xml new file mode 100644 index 000000000000..2140ebe47a18 --- /dev/null +++ b/app/src/gplay/res/color/outlined_button_text_color.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/app/src/gplay/res/color/text_button_text_color.xml b/app/src/gplay/res/color/text_button_text_color.xml new file mode 100644 index 000000000000..c40c98ad77f3 --- /dev/null +++ b/app/src/gplay/res/color/text_button_text_color.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/app/src/gplay/res/drawable-night/favorite.xml b/app/src/gplay/res/drawable-night/favorite.xml new file mode 100644 index 000000000000..f2728f1758d5 --- /dev/null +++ b/app/src/gplay/res/drawable-night/favorite.xml @@ -0,0 +1,18 @@ + + + + + diff --git a/app/src/gplay/res/drawable-night/nextcloud_logo.xml b/app/src/gplay/res/drawable-night/nextcloud_logo.xml new file mode 100644 index 000000000000..939c59c3e3a2 --- /dev/null +++ b/app/src/gplay/res/drawable-night/nextcloud_logo.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/drawable/account_circle_white.xml b/app/src/gplay/res/drawable/account_circle_white.xml new file mode 100644 index 000000000000..e8ea2101d106 --- /dev/null +++ b/app/src/gplay/res/drawable/account_circle_white.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/app/src/gplay/res/drawable/all_files.xml b/app/src/gplay/res/drawable/all_files.xml new file mode 100644 index 000000000000..9efb2dcd0327 --- /dev/null +++ b/app/src/gplay/res/drawable/all_files.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/bottom_sheet_drag_handle.xml b/app/src/gplay/res/drawable/bottom_sheet_drag_handle.xml new file mode 100644 index 000000000000..e060526252b1 --- /dev/null +++ b/app/src/gplay/res/drawable/bottom_sheet_drag_handle.xml @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/app/src/gplay/res/drawable/dialog_background.xml b/app/src/gplay/res/drawable/dialog_background.xml new file mode 100644 index 000000000000..6f14d4c3f227 --- /dev/null +++ b/app/src/gplay/res/drawable/dialog_background.xml @@ -0,0 +1,18 @@ + + + + + + + + + diff --git a/app/src/gplay/res/drawable/dialog_item_background.xml b/app/src/gplay/res/drawable/dialog_item_background.xml new file mode 100644 index 000000000000..cbcdd3ff5263 --- /dev/null +++ b/app/src/gplay/res/drawable/dialog_item_background.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + diff --git a/app/src/gplay/res/drawable/e2e_border.xml b/app/src/gplay/res/drawable/e2e_border.xml new file mode 100644 index 000000000000..a545c168e793 --- /dev/null +++ b/app/src/gplay/res/drawable/e2e_border.xml @@ -0,0 +1,14 @@ + + + + + + diff --git a/app/src/gplay/res/drawable/exo_divider.xml b/app/src/gplay/res/drawable/exo_divider.xml new file mode 100644 index 000000000000..205032f4f73d --- /dev/null +++ b/app/src/gplay/res/drawable/exo_divider.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/exo_ic_check.xml b/app/src/gplay/res/drawable/exo_ic_check.xml new file mode 100644 index 000000000000..d045c84f5db5 --- /dev/null +++ b/app/src/gplay/res/drawable/exo_ic_check.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/exo_styled_controls_audiotrack.xml b/app/src/gplay/res/drawable/exo_styled_controls_audiotrack.xml new file mode 100644 index 000000000000..fd50f84d0482 --- /dev/null +++ b/app/src/gplay/res/drawable/exo_styled_controls_audiotrack.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/exo_styled_controls_pause.xml b/app/src/gplay/res/drawable/exo_styled_controls_pause.xml new file mode 100644 index 000000000000..c45c39df05da --- /dev/null +++ b/app/src/gplay/res/drawable/exo_styled_controls_pause.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/exo_styled_controls_play.xml b/app/src/gplay/res/drawable/exo_styled_controls_play.xml new file mode 100644 index 000000000000..1956ab230365 --- /dev/null +++ b/app/src/gplay/res/drawable/exo_styled_controls_play.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/exo_styled_controls_settings.xml b/app/src/gplay/res/drawable/exo_styled_controls_settings.xml new file mode 100644 index 000000000000..d0a127203c85 --- /dev/null +++ b/app/src/gplay/res/drawable/exo_styled_controls_settings.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/exo_styled_controls_simple_fastforward.xml b/app/src/gplay/res/drawable/exo_styled_controls_simple_fastforward.xml new file mode 100644 index 000000000000..11002787d609 --- /dev/null +++ b/app/src/gplay/res/drawable/exo_styled_controls_simple_fastforward.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/exo_styled_controls_simple_rewind.xml b/app/src/gplay/res/drawable/exo_styled_controls_simple_rewind.xml new file mode 100644 index 000000000000..c7d2782ea707 --- /dev/null +++ b/app/src/gplay/res/drawable/exo_styled_controls_simple_rewind.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/exo_styled_controls_speed.xml b/app/src/gplay/res/drawable/exo_styled_controls_speed.xml new file mode 100644 index 000000000000..e10f5e2223e9 --- /dev/null +++ b/app/src/gplay/res/drawable/exo_styled_controls_speed.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/exo_thumb.xml b/app/src/gplay/res/drawable/exo_thumb.xml new file mode 100644 index 000000000000..d31859726d0a --- /dev/null +++ b/app/src/gplay/res/drawable/exo_thumb.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/app/src/gplay/res/drawable/favorite.xml b/app/src/gplay/res/drawable/favorite.xml new file mode 100644 index 000000000000..44a725f11469 --- /dev/null +++ b/app/src/gplay/res/drawable/favorite.xml @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/app/src/gplay/res/drawable/file.xml b/app/src/gplay/res/drawable/file.xml new file mode 100644 index 000000000000..bb46adbe85de --- /dev/null +++ b/app/src/gplay/res/drawable/file.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/file_calendar.xml b/app/src/gplay/res/drawable/file_calendar.xml new file mode 100644 index 000000000000..d8b720a581e1 --- /dev/null +++ b/app/src/gplay/res/drawable/file_calendar.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/file_image.xml b/app/src/gplay/res/drawable/file_image.xml new file mode 100644 index 000000000000..aaee3cf2eba8 --- /dev/null +++ b/app/src/gplay/res/drawable/file_image.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/file_multiple.xml b/app/src/gplay/res/drawable/file_multiple.xml new file mode 100644 index 000000000000..942c9373d4bc --- /dev/null +++ b/app/src/gplay/res/drawable/file_multiple.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/filelist_action_icon_background.xml b/app/src/gplay/res/drawable/filelist_action_icon_background.xml new file mode 100644 index 000000000000..ad4d33647d47 --- /dev/null +++ b/app/src/gplay/res/drawable/filelist_action_icon_background.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + diff --git a/app/src/gplay/res/drawable/folder.xml b/app/src/gplay/res/drawable/folder.xml new file mode 100644 index 000000000000..8b4fc8a42890 --- /dev/null +++ b/app/src/gplay/res/drawable/folder.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/grid_mode_item_background.xml b/app/src/gplay/res/drawable/grid_mode_item_background.xml new file mode 100644 index 000000000000..c3bb6301c093 --- /dev/null +++ b/app/src/gplay/res/drawable/grid_mode_item_background.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + diff --git a/app/src/gplay/res/drawable/grid_mode_item_outline.xml b/app/src/gplay/res/drawable/grid_mode_item_outline.xml new file mode 100644 index 000000000000..670be17089d1 --- /dev/null +++ b/app/src/gplay/res/drawable/grid_mode_item_outline.xml @@ -0,0 +1,12 @@ + + + + + + + diff --git a/app/src/gplay/res/drawable/grid_mode_more_button_background.xml b/app/src/gplay/res/drawable/grid_mode_more_button_background.xml new file mode 100644 index 000000000000..3dd44347922f --- /dev/null +++ b/app/src/gplay/res/drawable/grid_mode_more_button_background.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/drawable/grid_mode_selected_item_background.xml b/app/src/gplay/res/drawable/grid_mode_selected_item_background.xml new file mode 100644 index 000000000000..756aa8df241b --- /dev/null +++ b/app/src/gplay/res/drawable/grid_mode_selected_item_background.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + diff --git a/app/src/gplay/res/drawable/ic_action_create_dir.xml b/app/src/gplay/res/drawable/ic_action_create_dir.xml new file mode 100644 index 000000000000..2141f7c78e49 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_action_create_dir.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_action_upload.xml b/app/src/gplay/res/drawable/ic_action_upload.xml new file mode 100644 index 000000000000..c8a9e0631b0b --- /dev/null +++ b/app/src/gplay/res/drawable/ic_action_upload.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_activity.xml b/app/src/gplay/res/drawable/ic_activity.xml new file mode 100644 index 000000000000..2cf83e115e53 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_activity.xml @@ -0,0 +1,21 @@ + + + + + + + + + diff --git a/app/src/gplay/res/drawable/ic_alphabetical_asc.xml b/app/src/gplay/res/drawable/ic_alphabetical_asc.xml new file mode 100644 index 000000000000..39545cafc5fc --- /dev/null +++ b/app/src/gplay/res/drawable/ic_alphabetical_asc.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_alphabetical_desc.xml b/app/src/gplay/res/drawable/ic_alphabetical_desc.xml new file mode 100644 index 000000000000..f6c98c61d5f6 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_alphabetical_desc.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_arrow_back.xml b/app/src/gplay/res/drawable/ic_arrow_back.xml new file mode 100644 index 000000000000..114ef8a5760e --- /dev/null +++ b/app/src/gplay/res/drawable/ic_arrow_back.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_assistant.xml b/app/src/gplay/res/drawable/ic_assistant.xml new file mode 100644 index 000000000000..314febca6a85 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_assistant.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_camera.xml b/app/src/gplay/res/drawable/ic_camera.xml new file mode 100644 index 000000000000..eaaaecd77eb6 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_camera.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_checkbox_blank_outline.xml b/app/src/gplay/res/drawable/ic_checkbox_blank_outline.xml new file mode 100644 index 000000000000..acbdc3c8bb72 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_checkbox_blank_outline.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_checkbox_marked.xml b/app/src/gplay/res/drawable/ic_checkbox_marked.xml new file mode 100644 index 000000000000..d249debaeaab --- /dev/null +++ b/app/src/gplay/res/drawable/ic_checkbox_marked.xml @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/app/src/gplay/res/drawable/ic_close_search.xml b/app/src/gplay/res/drawable/ic_close_search.xml new file mode 100644 index 000000000000..f5be8d6d699d --- /dev/null +++ b/app/src/gplay/res/drawable/ic_close_search.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_cloud.xml b/app/src/gplay/res/drawable/ic_cloud.xml new file mode 100644 index 000000000000..1fadad4368de --- /dev/null +++ b/app/src/gplay/res/drawable/ic_cloud.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_cloud_download.xml b/app/src/gplay/res/drawable/ic_cloud_download.xml new file mode 100644 index 000000000000..37592e253cd6 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_cloud_download.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_cloud_upload.xml b/app/src/gplay/res/drawable/ic_cloud_upload.xml new file mode 100644 index 000000000000..c4bc7f3dd6ba --- /dev/null +++ b/app/src/gplay/res/drawable/ic_cloud_upload.xml @@ -0,0 +1,18 @@ + + + + + + + diff --git a/app/src/gplay/res/drawable/ic_content_copy.xml b/app/src/gplay/res/drawable/ic_content_copy.xml new file mode 100644 index 000000000000..3cfc1e42d971 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_content_copy.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_delete.xml b/app/src/gplay/res/drawable/ic_delete.xml new file mode 100644 index 000000000000..8a0b2f618a58 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_delete.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_dots_vertical.xml b/app/src/gplay/res/drawable/ic_dots_vertical.xml new file mode 100644 index 000000000000..0e9c6496f86a --- /dev/null +++ b/app/src/gplay/res/drawable/ic_dots_vertical.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_edit.xml b/app/src/gplay/res/drawable/ic_edit.xml new file mode 100644 index 000000000000..b70c7eae487c --- /dev/null +++ b/app/src/gplay/res/drawable/ic_edit.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_email.xml b/app/src/gplay/res/drawable/ic_email.xml new file mode 100644 index 000000000000..5c1a5a36f8da --- /dev/null +++ b/app/src/gplay/res/drawable/ic_email.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_file_action_cancel_sync.xml b/app/src/gplay/res/drawable/ic_file_action_cancel_sync.xml new file mode 100644 index 000000000000..0a9173c21d08 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_file_action_cancel_sync.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_file_action_download_file.xml b/app/src/gplay/res/drawable/ic_file_action_download_file.xml new file mode 100644 index 000000000000..9f5e50a293db --- /dev/null +++ b/app/src/gplay/res/drawable/ic_file_action_download_file.xml @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/app/src/gplay/res/drawable/ic_file_action_edit.xml b/app/src/gplay/res/drawable/ic_file_action_edit.xml new file mode 100644 index 000000000000..43233ac3db1d --- /dev/null +++ b/app/src/gplay/res/drawable/ic_file_action_edit.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_file_action_export_file.xml b/app/src/gplay/res/drawable/ic_file_action_export_file.xml new file mode 100644 index 000000000000..d331d6fb1b57 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_file_action_export_file.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_file_action_favorite.xml b/app/src/gplay/res/drawable/ic_file_action_favorite.xml new file mode 100644 index 000000000000..f03c59e85bf1 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_file_action_favorite.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_file_action_lock_file.xml b/app/src/gplay/res/drawable/ic_file_action_lock_file.xml new file mode 100644 index 000000000000..f83bf38fd62d --- /dev/null +++ b/app/src/gplay/res/drawable/ic_file_action_lock_file.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_file_action_move_or_copy.xml b/app/src/gplay/res/drawable/ic_file_action_move_or_copy.xml new file mode 100644 index 000000000000..1d9b33e52e55 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_file_action_move_or_copy.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_file_action_open_file_with.xml b/app/src/gplay/res/drawable/ic_file_action_open_file_with.xml new file mode 100644 index 000000000000..ebd25250a8ec --- /dev/null +++ b/app/src/gplay/res/drawable/ic_file_action_open_file_with.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_file_action_pin_to_homescreen.xml b/app/src/gplay/res/drawable/ic_file_action_pin_to_homescreen.xml new file mode 100644 index 000000000000..12bad2c11102 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_file_action_pin_to_homescreen.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_file_action_remove_file.xml b/app/src/gplay/res/drawable/ic_file_action_remove_file.xml new file mode 100644 index 000000000000..8a0b2f618a58 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_file_action_remove_file.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_file_action_rename_file.xml b/app/src/gplay/res/drawable/ic_file_action_rename_file.xml new file mode 100644 index 000000000000..e2a4df854a82 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_file_action_rename_file.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_file_action_see_details.xml b/app/src/gplay/res/drawable/ic_file_action_see_details.xml new file mode 100644 index 000000000000..62f45aa6f038 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_file_action_see_details.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_file_action_select_all.xml b/app/src/gplay/res/drawable/ic_file_action_select_all.xml new file mode 100644 index 000000000000..a286b9168a6e --- /dev/null +++ b/app/src/gplay/res/drawable/ic_file_action_select_all.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_file_action_select_none.xml b/app/src/gplay/res/drawable/ic_file_action_select_none.xml new file mode 100644 index 000000000000..929269719e69 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_file_action_select_none.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_file_action_set_as_wallpaper.xml b/app/src/gplay/res/drawable/ic_file_action_set_as_wallpaper.xml new file mode 100644 index 000000000000..fdaa13d8c77d --- /dev/null +++ b/app/src/gplay/res/drawable/ic_file_action_set_as_wallpaper.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_file_action_set_encrypted.xml b/app/src/gplay/res/drawable/ic_file_action_set_encrypted.xml new file mode 100644 index 000000000000..ca99659b4629 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_file_action_set_encrypted.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_file_action_share_file.xml b/app/src/gplay/res/drawable/ic_file_action_share_file.xml new file mode 100644 index 000000000000..a0e14edfa8b2 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_file_action_share_file.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_file_action_stream_media.xml b/app/src/gplay/res/drawable/ic_file_action_stream_media.xml new file mode 100644 index 000000000000..9e4d0662e523 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_file_action_stream_media.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_file_action_sync_file.xml b/app/src/gplay/res/drawable/ic_file_action_sync_file.xml new file mode 100644 index 000000000000..297aa442036f --- /dev/null +++ b/app/src/gplay/res/drawable/ic_file_action_sync_file.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_file_action_unlock_file.xml b/app/src/gplay/res/drawable/ic_file_action_unlock_file.xml new file mode 100644 index 000000000000..d4724ca67180 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_file_action_unlock_file.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_file_action_unset_encrypted.xml b/app/src/gplay/res/drawable/ic_file_action_unset_encrypted.xml new file mode 100644 index 000000000000..20a8f656fd71 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_file_action_unset_encrypted.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_file_action_unset_favorite.xml b/app/src/gplay/res/drawable/ic_file_action_unset_favorite.xml new file mode 100644 index 000000000000..f38d5c4ea3da --- /dev/null +++ b/app/src/gplay/res/drawable/ic_file_action_unset_favorite.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_filelist_shared_via_link.xml b/app/src/gplay/res/drawable/ic_filelist_shared_via_link.xml new file mode 100644 index 000000000000..f4ef3c4ff1cc --- /dev/null +++ b/app/src/gplay/res/drawable/ic_filelist_shared_via_link.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/app/src/gplay/res/drawable/ic_filelist_shared_via_users.xml b/app/src/gplay/res/drawable/ic_filelist_shared_via_users.xml new file mode 100644 index 000000000000..e0389e0ee4b3 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_filelist_shared_via_users.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + diff --git a/app/src/gplay/res/drawable/ic_filelist_unshared.xml b/app/src/gplay/res/drawable/ic_filelist_unshared.xml new file mode 100644 index 000000000000..e11da31a265e --- /dev/null +++ b/app/src/gplay/res/drawable/ic_filelist_unshared.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_filelist_unshared_grid_mode.xml b/app/src/gplay/res/drawable/ic_filelist_unshared_grid_mode.xml new file mode 100644 index 000000000000..64b2f6d3af01 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_filelist_unshared_grid_mode.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_folder_overlay_account_group.xml b/app/src/gplay/res/drawable/ic_folder_overlay_account_group.xml new file mode 100644 index 000000000000..05e232d6895f --- /dev/null +++ b/app/src/gplay/res/drawable/ic_folder_overlay_account_group.xml @@ -0,0 +1,24 @@ + + + + + + + + + diff --git a/app/src/gplay/res/drawable/ic_folder_overlay_external.xml b/app/src/gplay/res/drawable/ic_folder_overlay_external.xml new file mode 100644 index 000000000000..7eb67c1c67cb --- /dev/null +++ b/app/src/gplay/res/drawable/ic_folder_overlay_external.xml @@ -0,0 +1,22 @@ + + + + + + + diff --git a/app/src/gplay/res/drawable/ic_folder_overlay_key.xml b/app/src/gplay/res/drawable/ic_folder_overlay_key.xml new file mode 100644 index 000000000000..181769358a26 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_folder_overlay_key.xml @@ -0,0 +1,22 @@ + + + + + + + diff --git a/app/src/gplay/res/drawable/ic_folder_overlay_link.xml b/app/src/gplay/res/drawable/ic_folder_overlay_link.xml new file mode 100644 index 000000000000..05e232d6895f --- /dev/null +++ b/app/src/gplay/res/drawable/ic_folder_overlay_link.xml @@ -0,0 +1,24 @@ + + + + + + + + + diff --git a/app/src/gplay/res/drawable/ic_folder_overlay_lock.xml b/app/src/gplay/res/drawable/ic_folder_overlay_lock.xml new file mode 100644 index 000000000000..db30297451b6 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_folder_overlay_lock.xml @@ -0,0 +1,22 @@ + + + + + + + diff --git a/app/src/gplay/res/drawable/ic_folder_overlay_share.xml b/app/src/gplay/res/drawable/ic_folder_overlay_share.xml new file mode 100644 index 000000000000..05e232d6895f --- /dev/null +++ b/app/src/gplay/res/drawable/ic_folder_overlay_share.xml @@ -0,0 +1,24 @@ + + + + + + + + + diff --git a/app/src/gplay/res/drawable/ic_folder_overlay_upload.xml b/app/src/gplay/res/drawable/ic_folder_overlay_upload.xml new file mode 100644 index 000000000000..afe3192f83aa --- /dev/null +++ b/app/src/gplay/res/drawable/ic_folder_overlay_upload.xml @@ -0,0 +1,24 @@ + + + + + + + + + diff --git a/app/src/gplay/res/drawable/ic_gallery_set_media_folder.xml b/app/src/gplay/res/drawable/ic_gallery_set_media_folder.xml new file mode 100644 index 000000000000..fdaa13d8c77d --- /dev/null +++ b/app/src/gplay/res/drawable/ic_gallery_set_media_folder.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_gallery_show_photos.xml b/app/src/gplay/res/drawable/ic_gallery_show_photos.xml new file mode 100644 index 000000000000..eaaaecd77eb6 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_gallery_show_photos.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_gallery_show_videos.xml b/app/src/gplay/res/drawable/ic_gallery_show_videos.xml new file mode 100644 index 000000000000..a8ea6b923ae0 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_gallery_show_videos.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_gallery_tick.xml b/app/src/gplay/res/drawable/ic_gallery_tick.xml new file mode 100644 index 000000000000..05a9c15ca270 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_gallery_tick.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_grid_mode_more.xml b/app/src/gplay/res/drawable/ic_grid_mode_more.xml new file mode 100644 index 000000000000..9d9291500985 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_grid_mode_more.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_history.xml b/app/src/gplay/res/drawable/ic_history.xml new file mode 100644 index 000000000000..abcf1d0d9b45 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_history.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_import.xml b/app/src/gplay/res/drawable/ic_import.xml new file mode 100644 index 000000000000..b7523313a2c1 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_import.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_launcher_foreground.xml b/app/src/gplay/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 000000000000..7d75ec0175be --- /dev/null +++ b/app/src/gplay/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,23 @@ + + + + + + + diff --git a/app/src/gplay/res/drawable/ic_link.xml b/app/src/gplay/res/drawable/ic_link.xml new file mode 100644 index 000000000000..b1666422bcc6 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_link.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_list_empty_create_folder.xml b/app/src/gplay/res/drawable/ic_list_empty_create_folder.xml new file mode 100644 index 000000000000..2af06159b73c --- /dev/null +++ b/app/src/gplay/res/drawable/ic_list_empty_create_folder.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_list_empty_error.xml b/app/src/gplay/res/drawable/ic_list_empty_error.xml new file mode 100644 index 000000000000..4b64a4e49553 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_list_empty_error.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_list_empty_folder.xml b/app/src/gplay/res/drawable/ic_list_empty_folder.xml new file mode 100644 index 000000000000..bbeb3f1d05ee --- /dev/null +++ b/app/src/gplay/res/drawable/ic_list_empty_folder.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_list_empty_recent.xml b/app/src/gplay/res/drawable/ic_list_empty_recent.xml new file mode 100644 index 000000000000..676879e62d6e --- /dev/null +++ b/app/src/gplay/res/drawable/ic_list_empty_recent.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_list_empty_shared.xml b/app/src/gplay/res/drawable/ic_list_empty_shared.xml new file mode 100644 index 000000000000..5cf82c43f9f6 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_list_empty_shared.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_menu.xml b/app/src/gplay/res/drawable/ic_menu.xml new file mode 100644 index 000000000000..d3ae8c8b3080 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_menu.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_modification_asc.xml b/app/src/gplay/res/drawable/ic_modification_asc.xml new file mode 100644 index 000000000000..19f367facb48 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_modification_asc.xml @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/app/src/gplay/res/drawable/ic_modification_desc.xml b/app/src/gplay/res/drawable/ic_modification_desc.xml new file mode 100644 index 000000000000..58c619363535 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_modification_desc.xml @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/app/src/gplay/res/drawable/ic_pause.xml b/app/src/gplay/res/drawable/ic_pause.xml new file mode 100644 index 000000000000..af11ea628327 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_pause.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/app/src/gplay/res/drawable/ic_play.xml b/app/src/gplay/res/drawable/ic_play.xml new file mode 100644 index 000000000000..d151770705cf --- /dev/null +++ b/app/src/gplay/res/drawable/ic_play.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/app/src/gplay/res/drawable/ic_plus.xml b/app/src/gplay/res/drawable/ic_plus.xml new file mode 100644 index 000000000000..1990e792ad8f --- /dev/null +++ b/app/src/gplay/res/drawable/ic_plus.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_progress_bar_login_indeterminate.xml b/app/src/gplay/res/drawable/ic_progress_bar_login_indeterminate.xml new file mode 100644 index 000000000000..0a5a500b19f7 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_progress_bar_login_indeterminate.xml @@ -0,0 +1,21 @@ + + + + + + + diff --git a/app/src/gplay/res/drawable/ic_scan_document.xml b/app/src/gplay/res/drawable/ic_scan_document.xml new file mode 100644 index 000000000000..8799c9510fbb --- /dev/null +++ b/app/src/gplay/res/drawable/ic_scan_document.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_search.xml b/app/src/gplay/res/drawable/ic_search.xml new file mode 100644 index 000000000000..f6d35d2f2bcc --- /dev/null +++ b/app/src/gplay/res/drawable/ic_search.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_select_all.xml b/app/src/gplay/res/drawable/ic_select_all.xml new file mode 100644 index 000000000000..50db787d70ae --- /dev/null +++ b/app/src/gplay/res/drawable/ic_select_all.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_share.xml b/app/src/gplay/res/drawable/ic_share.xml new file mode 100644 index 000000000000..5cf82c43f9f6 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_share.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_share_settings.xml b/app/src/gplay/res/drawable/ic_share_settings.xml new file mode 100644 index 000000000000..91a6d5e62bbd --- /dev/null +++ b/app/src/gplay/res/drawable/ic_share_settings.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_size_asc.xml b/app/src/gplay/res/drawable/ic_size_asc.xml new file mode 100644 index 000000000000..08e0b390792f --- /dev/null +++ b/app/src/gplay/res/drawable/ic_size_asc.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_size_desc.xml b/app/src/gplay/res/drawable/ic_size_desc.xml new file mode 100644 index 000000000000..d0ffd2c9d99e --- /dev/null +++ b/app/src/gplay/res/drawable/ic_size_desc.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_synced.xml b/app/src/gplay/res/drawable/ic_synced.xml new file mode 100644 index 000000000000..31b565901f3b --- /dev/null +++ b/app/src/gplay/res/drawable/ic_synced.xml @@ -0,0 +1,21 @@ + + + + + + diff --git a/app/src/gplay/res/drawable/ic_tick.xml b/app/src/gplay/res/drawable/ic_tick.xml new file mode 100644 index 000000000000..f4716e25109c --- /dev/null +++ b/app/src/gplay/res/drawable/ic_tick.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_user.xml b/app/src/gplay/res/drawable/ic_user.xml new file mode 100644 index 000000000000..6517166eeba1 --- /dev/null +++ b/app/src/gplay/res/drawable/ic_user.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_view_list.xml b/app/src/gplay/res/drawable/ic_view_list.xml new file mode 100644 index 000000000000..3a287644bd8f --- /dev/null +++ b/app/src/gplay/res/drawable/ic_view_list.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ic_view_module.xml b/app/src/gplay/res/drawable/ic_view_module.xml new file mode 100644 index 000000000000..8e96d7dfb9bb --- /dev/null +++ b/app/src/gplay/res/drawable/ic_view_module.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/ionos_logo.xml b/app/src/gplay/res/drawable/ionos_logo.xml new file mode 100644 index 000000000000..9f971f82dba3 --- /dev/null +++ b/app/src/gplay/res/drawable/ionos_logo.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/drawable/logo.xml b/app/src/gplay/res/drawable/logo.xml new file mode 100644 index 000000000000..9f971f82dba3 --- /dev/null +++ b/app/src/gplay/res/drawable/logo.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/drawable/nav_favorites.xml b/app/src/gplay/res/drawable/nav_favorites.xml new file mode 100644 index 000000000000..f03c59e85bf1 --- /dev/null +++ b/app/src/gplay/res/drawable/nav_favorites.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/nav_notifications.xml b/app/src/gplay/res/drawable/nav_notifications.xml new file mode 100644 index 000000000000..f8c12d5af7dc --- /dev/null +++ b/app/src/gplay/res/drawable/nav_notifications.xml @@ -0,0 +1,16 @@ + + + + diff --git a/app/src/gplay/res/drawable/nav_on_device.xml b/app/src/gplay/res/drawable/nav_on_device.xml new file mode 100644 index 000000000000..902842d03802 --- /dev/null +++ b/app/src/gplay/res/drawable/nav_on_device.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/nav_photos.xml b/app/src/gplay/res/drawable/nav_photos.xml new file mode 100644 index 000000000000..fdaa13d8c77d --- /dev/null +++ b/app/src/gplay/res/drawable/nav_photos.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/nav_settings.xml b/app/src/gplay/res/drawable/nav_settings.xml new file mode 100644 index 000000000000..dfa36583d70f --- /dev/null +++ b/app/src/gplay/res/drawable/nav_settings.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/nav_shared.xml b/app/src/gplay/res/drawable/nav_shared.xml new file mode 100644 index 000000000000..d136bc23398e --- /dev/null +++ b/app/src/gplay/res/drawable/nav_shared.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/nav_trashbin.xml b/app/src/gplay/res/drawable/nav_trashbin.xml new file mode 100644 index 000000000000..75e40f031021 --- /dev/null +++ b/app/src/gplay/res/drawable/nav_trashbin.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/nextcloud_logo.xml b/app/src/gplay/res/drawable/nextcloud_logo.xml new file mode 100644 index 000000000000..41f4c5d33874 --- /dev/null +++ b/app/src/gplay/res/drawable/nextcloud_logo.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/drawable/notification_icon.xml b/app/src/gplay/res/drawable/notification_icon.xml new file mode 100644 index 000000000000..6c1ccab18953 --- /dev/null +++ b/app/src/gplay/res/drawable/notification_icon.xml @@ -0,0 +1,17 @@ + + + + \ No newline at end of file diff --git a/app/src/gplay/res/drawable/notifications_icon.xml b/app/src/gplay/res/drawable/notifications_icon.xml new file mode 100644 index 000000000000..7fbc42b42e90 --- /dev/null +++ b/app/src/gplay/res/drawable/notifications_icon.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/drawable/progress_bar_login_indeterminate.xml b/app/src/gplay/res/drawable/progress_bar_login_indeterminate.xml new file mode 100644 index 000000000000..01762da59db2 --- /dev/null +++ b/app/src/gplay/res/drawable/progress_bar_login_indeterminate.xml @@ -0,0 +1,14 @@ + + + + + + diff --git a/app/src/gplay/res/drawable/uploads.xml b/app/src/gplay/res/drawable/uploads.xml new file mode 100644 index 000000000000..c26710c81044 --- /dev/null +++ b/app/src/gplay/res/drawable/uploads.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/gplay/res/layout/account_setup.xml b/app/src/gplay/res/layout/account_setup.xml new file mode 100644 index 000000000000..4e669f1b2aef --- /dev/null +++ b/app/src/gplay/res/layout/account_setup.xml @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/layout/account_setup_webview.xml b/app/src/gplay/res/layout/account_setup_webview.xml new file mode 100644 index 000000000000..9e4286dbfe19 --- /dev/null +++ b/app/src/gplay/res/layout/account_setup_webview.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + diff --git a/app/src/gplay/res/layout/activity_preview_media.xml b/app/src/gplay/res/layout/activity_preview_media.xml new file mode 100644 index 000000000000..73db24d5bb30 --- /dev/null +++ b/app/src/gplay/res/layout/activity_preview_media.xml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/layout/activity_splash.xml b/app/src/gplay/res/layout/activity_splash.xml new file mode 100644 index 000000000000..a26af2ef1c45 --- /dev/null +++ b/app/src/gplay/res/layout/activity_splash.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/gplay/res/layout/backup_fragment.xml b/app/src/gplay/res/layout/backup_fragment.xml new file mode 100644 index 000000000000..5236968c01b6 --- /dev/null +++ b/app/src/gplay/res/layout/backup_fragment.xml @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/layout/deep_link_login.xml b/app/src/gplay/res/layout/deep_link_login.xml new file mode 100644 index 000000000000..1dbf1c43d6fa --- /dev/null +++ b/app/src/gplay/res/layout/deep_link_login.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/layout/drawer.xml b/app/src/gplay/res/layout/drawer.xml new file mode 100644 index 000000000000..82430c48c83d --- /dev/null +++ b/app/src/gplay/res/layout/drawer.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/layout/empty_list_media.xml b/app/src/gplay/res/layout/empty_list_media.xml new file mode 100644 index 000000000000..9c6e8c95fc45 --- /dev/null +++ b/app/src/gplay/res/layout/empty_list_media.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/layout/exo_player_control_view.xml b/app/src/gplay/res/layout/exo_player_control_view.xml new file mode 100644 index 000000000000..495c0861183e --- /dev/null +++ b/app/src/gplay/res/layout/exo_player_control_view.xml @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/layout/exo_player_view.xml b/app/src/gplay/res/layout/exo_player_view.xml new file mode 100644 index 000000000000..930b014b1d30 --- /dev/null +++ b/app/src/gplay/res/layout/exo_player_view.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/layout/file_actions_bottom_sheet.xml b/app/src/gplay/res/layout/file_actions_bottom_sheet.xml new file mode 100644 index 000000000000..c3e243d8225e --- /dev/null +++ b/app/src/gplay/res/layout/file_actions_bottom_sheet.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/layout/file_actions_bottom_sheet_item.xml b/app/src/gplay/res/layout/file_actions_bottom_sheet_item.xml new file mode 100644 index 000000000000..290f012e9ce0 --- /dev/null +++ b/app/src/gplay/res/layout/file_actions_bottom_sheet_item.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/layout/file_details_fragment.xml b/app/src/gplay/res/layout/file_details_fragment.xml new file mode 100644 index 000000000000..d9b76f9d2b06 --- /dev/null +++ b/app/src/gplay/res/layout/file_details_fragment.xml @@ -0,0 +1,236 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/layout/file_details_share_link_share_item.xml b/app/src/gplay/res/layout/file_details_share_link_share_item.xml new file mode 100644 index 000000000000..0ddd4f10e1be --- /dev/null +++ b/app/src/gplay/res/layout/file_details_share_link_share_item.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/layout/file_details_sharing_fragment.xml b/app/src/gplay/res/layout/file_details_sharing_fragment.xml new file mode 100644 index 000000000000..90ebe6f77b49 --- /dev/null +++ b/app/src/gplay/res/layout/file_details_sharing_fragment.xml @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/layout/file_details_sharing_menu_bottom_sheet_fragment.xml b/app/src/gplay/res/layout/file_details_sharing_menu_bottom_sheet_fragment.xml new file mode 100644 index 000000000000..552d21cfef80 --- /dev/null +++ b/app/src/gplay/res/layout/file_details_sharing_menu_bottom_sheet_fragment.xml @@ -0,0 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/layout/fragment_gallery_bottom_sheet.xml b/app/src/gplay/res/layout/fragment_gallery_bottom_sheet.xml new file mode 100644 index 000000000000..139a9a827c6b --- /dev/null +++ b/app/src/gplay/res/layout/fragment_gallery_bottom_sheet.xml @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/layout/grid_item.xml b/app/src/gplay/res/layout/grid_item.xml new file mode 100644 index 000000000000..b4c31aa3c477 --- /dev/null +++ b/app/src/gplay/res/layout/grid_item.xml @@ -0,0 +1,191 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/layout/info_box.xml b/app/src/gplay/res/layout/info_box.xml new file mode 100644 index 000000000000..40d99656dd68 --- /dev/null +++ b/app/src/gplay/res/layout/info_box.xml @@ -0,0 +1,36 @@ + + + + + + + diff --git a/app/src/gplay/res/layout/item_quick_share_permissions.xml b/app/src/gplay/res/layout/item_quick_share_permissions.xml new file mode 100644 index 000000000000..410088009941 --- /dev/null +++ b/app/src/gplay/res/layout/item_quick_share_permissions.xml @@ -0,0 +1,44 @@ + + + + + + + + + diff --git a/app/src/gplay/res/layout/list_item.xml b/app/src/gplay/res/layout/list_item.xml new file mode 100644 index 000000000000..53c76999d3d2 --- /dev/null +++ b/app/src/gplay/res/layout/list_item.xml @@ -0,0 +1,300 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/layout/login_flow_info_layout_v2.xml b/app/src/gplay/res/layout/login_flow_info_layout_v2.xml new file mode 100644 index 000000000000..3227876ee40e --- /dev/null +++ b/app/src/gplay/res/layout/login_flow_info_layout_v2.xml @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/gplay/res/layout/media_control.xml b/app/src/gplay/res/layout/media_control.xml new file mode 100644 index 000000000000..2b13b15d87b3 --- /dev/null +++ b/app/src/gplay/res/layout/media_control.xml @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/layout/quick_sharing_permissions_bottom_sheet_fragment.xml b/app/src/gplay/res/layout/quick_sharing_permissions_bottom_sheet_fragment.xml new file mode 100644 index 000000000000..ae1fd24ba225 --- /dev/null +++ b/app/src/gplay/res/layout/quick_sharing_permissions_bottom_sheet_fragment.xml @@ -0,0 +1,27 @@ + + + + + + + + + diff --git a/app/src/gplay/res/layout/settings_preference_first_group.xml b/app/src/gplay/res/layout/settings_preference_first_group.xml new file mode 100644 index 000000000000..f207114a078e --- /dev/null +++ b/app/src/gplay/res/layout/settings_preference_first_group.xml @@ -0,0 +1,26 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/gplay/res/layout/settings_preference_group.xml b/app/src/gplay/res/layout/settings_preference_group.xml new file mode 100644 index 000000000000..56d0dd055f0e --- /dev/null +++ b/app/src/gplay/res/layout/settings_preference_group.xml @@ -0,0 +1,25 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/gplay/res/layout/settings_preference_list_item.xml b/app/src/gplay/res/layout/settings_preference_list_item.xml new file mode 100644 index 000000000000..5fddbb9bf057 --- /dev/null +++ b/app/src/gplay/res/layout/settings_preference_list_item.xml @@ -0,0 +1,34 @@ + + + + + + + + + diff --git a/app/src/gplay/res/layout/settings_preference_switcher.xml b/app/src/gplay/res/layout/settings_preference_switcher.xml new file mode 100644 index 000000000000..81b7886dd1fd --- /dev/null +++ b/app/src/gplay/res/layout/settings_preference_switcher.xml @@ -0,0 +1,53 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/gplay/res/layout/settings_privacy_switchers.xml b/app/src/gplay/res/layout/settings_privacy_switchers.xml new file mode 100644 index 000000000000..297c6bfceefc --- /dev/null +++ b/app/src/gplay/res/layout/settings_privacy_switchers.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/layout/simple_file_list_actions_bottom_sheet_fragment.xml b/app/src/gplay/res/layout/simple_file_list_actions_bottom_sheet_fragment.xml new file mode 100644 index 000000000000..197853d4bc9e --- /dev/null +++ b/app/src/gplay/res/layout/simple_file_list_actions_bottom_sheet_fragment.xml @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/layout/simple_spinner_item.xml b/app/src/gplay/res/layout/simple_spinner_item.xml new file mode 100644 index 000000000000..4b6e91765dd6 --- /dev/null +++ b/app/src/gplay/res/layout/simple_spinner_item.xml @@ -0,0 +1,16 @@ + + + + diff --git a/app/src/gplay/res/layout/sorting_order_fragment.xml b/app/src/gplay/res/layout/sorting_order_fragment.xml new file mode 100644 index 000000000000..ad8ff4cd8e72 --- /dev/null +++ b/app/src/gplay/res/layout/sorting_order_fragment.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/layout/synced_folders_item_header.xml b/app/src/gplay/res/layout/synced_folders_item_header.xml new file mode 100644 index 000000000000..14d99248b903 --- /dev/null +++ b/app/src/gplay/res/layout/synced_folders_item_header.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/layout/toolbar_standard.xml b/app/src/gplay/res/layout/toolbar_standard.xml new file mode 100644 index 000000000000..99c1ad152f52 --- /dev/null +++ b/app/src/gplay/res/layout/toolbar_standard.xml @@ -0,0 +1,233 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/layout/view_authorization_method.xml b/app/src/gplay/res/layout/view_authorization_method.xml new file mode 100644 index 000000000000..85d8ec412ce3 --- /dev/null +++ b/app/src/gplay/res/layout/view_authorization_method.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/gplay/res/layout/view_login_background.xml b/app/src/gplay/res/layout/view_login_background.xml new file mode 100644 index 000000000000..80064d2ab3c1 --- /dev/null +++ b/app/src/gplay/res/layout/view_login_background.xml @@ -0,0 +1,26 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/gplay/res/menu/partial_drawer_entries.xml b/app/src/gplay/res/menu/partial_drawer_entries.xml new file mode 100644 index 000000000000..5a9955fc8c15 --- /dev/null +++ b/app/src/gplay/res/menu/partial_drawer_entries.xml @@ -0,0 +1,146 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/gplay/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 000000000000..a1f8fa17e3c4 --- /dev/null +++ b/app/src/gplay/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/gplay/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/gplay/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 000000000000..448b38f276ae --- /dev/null +++ b/app/src/gplay/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/gplay/res/mipmap-hdpi/ic_launcher.webp b/app/src/gplay/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 000000000000..063c51d877a2 Binary files /dev/null and b/app/src/gplay/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/app/src/gplay/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/gplay/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 000000000000..da712edf87b0 Binary files /dev/null and b/app/src/gplay/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/app/src/gplay/res/mipmap-mdpi/ic_launcher.webp b/app/src/gplay/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 000000000000..3fe3cbdfb17d Binary files /dev/null and b/app/src/gplay/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/app/src/gplay/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/gplay/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 000000000000..eefd99007825 Binary files /dev/null and b/app/src/gplay/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/app/src/gplay/res/mipmap-xhdpi/ic_launcher.webp b/app/src/gplay/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 000000000000..7f731095ee14 Binary files /dev/null and b/app/src/gplay/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/app/src/gplay/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/gplay/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 000000000000..d3dc506789c7 Binary files /dev/null and b/app/src/gplay/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/app/src/gplay/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/gplay/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 000000000000..4972ec7f3318 Binary files /dev/null and b/app/src/gplay/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/app/src/gplay/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/gplay/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 000000000000..885d2b49ac23 Binary files /dev/null and b/app/src/gplay/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/app/src/gplay/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/gplay/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 000000000000..2648a2d213db Binary files /dev/null and b/app/src/gplay/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/app/src/gplay/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/gplay/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 000000000000..c8a15cadff46 Binary files /dev/null and b/app/src/gplay/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/app/src/gplay/res/values-de/ionos-setup.xml b/app/src/gplay/res/values-de/ionos-setup.xml new file mode 100644 index 000000000000..c7ef8f525d85 --- /dev/null +++ b/app/src/gplay/res/values-de/ionos-setup.xml @@ -0,0 +1,13 @@ + + + + + + https://wl.hidrive.com/easy/0142 + https://wl.hidrive.com/easy/0092 + diff --git a/app/src/gplay/res/values-de/ionos-strings.xml b/app/src/gplay/res/values-de/ionos-strings.xml new file mode 100644 index 000000000000..903098581320 --- /dev/null +++ b/app/src/gplay/res/values-de/ionos-strings.xml @@ -0,0 +1,24 @@ + + + + + + + Willkommen beim Cloud-Speicher + Login + Sie müssen sich über den Browser einloggen + Abbrechen + Wiederholen + + + Filtern + + + Verwendeter Speicher + + \ No newline at end of file diff --git a/app/src/gplay/res/values-es/ionos-setup.xml b/app/src/gplay/res/values-es/ionos-setup.xml new file mode 100644 index 000000000000..6b47802d9ef2 --- /dev/null +++ b/app/src/gplay/res/values-es/ionos-setup.xml @@ -0,0 +1,13 @@ + + + + + + https://wl.hidrive.com/easy/0152 + https://wl.hidrive.com/easy/0112 + diff --git a/app/src/gplay/res/values-es/ionos-strings.xml b/app/src/gplay/res/values-es/ionos-strings.xml new file mode 100644 index 000000000000..5c436328ec11 --- /dev/null +++ b/app/src/gplay/res/values-es/ionos-strings.xml @@ -0,0 +1,24 @@ + + + + + + + Bienvenido/a al almacenamiento en la nube + Iniciar sesión + Tienes que iniciar sesión en el navegador. + Cancelar + Volver a intentar + + + Filtro + + + Memoria utilizada + + \ No newline at end of file diff --git a/app/src/gplay/res/values-fr/ionos-setup.xml b/app/src/gplay/res/values-fr/ionos-setup.xml new file mode 100644 index 000000000000..e33dc6854637 --- /dev/null +++ b/app/src/gplay/res/values-fr/ionos-setup.xml @@ -0,0 +1,13 @@ + + + + + + https://wl.hidrive.com/easy/0122 + https://wl.hidrive.com/easy/0082 + diff --git a/app/src/gplay/res/values-fr/ionos-strings.xml b/app/src/gplay/res/values-fr/ionos-strings.xml new file mode 100644 index 000000000000..48196d5ae24b --- /dev/null +++ b/app/src/gplay/res/values-fr/ionos-strings.xml @@ -0,0 +1,24 @@ + + + + + + + Bienvenue sur votre cloud + Connexion + Un navigateur s\'ouvre pour le login + Annuler + Répéter + + + Filtre + + + Stockage occupé + + \ No newline at end of file diff --git a/app/src/gplay/res/values-land/ionos-dimens.xml b/app/src/gplay/res/values-land/ionos-dimens.xml new file mode 100644 index 000000000000..62bf3d0ab28b --- /dev/null +++ b/app/src/gplay/res/values-land/ionos-dimens.xml @@ -0,0 +1,15 @@ + + + + + + 0.05 + 0.4 + 0.65 + + \ No newline at end of file diff --git a/app/src/gplay/res/values-large/ionos-dimens.xml b/app/src/gplay/res/values-large/ionos-dimens.xml new file mode 100644 index 000000000000..a008276d3d92 --- /dev/null +++ b/app/src/gplay/res/values-large/ionos-dimens.xml @@ -0,0 +1,30 @@ + + + + + + + 36dp + 48dp + 36dp + 12dp + 48dp + 12dp + 12dp + 48dp + 8dp + 16sp + 14sp + + 16dp + 56dp + 72dp + 8dp + 80dp + + \ No newline at end of file diff --git a/app/src/gplay/res/values-night/ionos-colors.xml b/app/src/gplay/res/values-night/ionos-colors.xml new file mode 100644 index 000000000000..c1cf4fd29376 --- /dev/null +++ b/app/src/gplay/res/values-night/ionos-colors.xml @@ -0,0 +1,126 @@ + + + + + @color/ionos_default_text_color + @color/metallic_silver + @color/midnight_blue2 + @color/ionos_toolbar_color + @color/navy + @color/navy + @color/ionos_default_icon_color + + @color/navy + + @color/dark_slate_grey + @color/dark_slate_grey + @color/dark_slate_grey + @color/deep_koamaru + @color/jungle_mist + + @color/midnight_blue2 + @color/ghost_white + @color/ghost_white + @color/geyser + @color/curious_blue + @color/orange + + @color/dodger_blue + @color/royal_blue + @color/dark_slate_grey + + @color/white + @color/white + @color/dusk + + @color/transparent + @color/ghost_white + @color/transparent + @color/ghost_white + @color/ghost_white + @color/dusk + @color/ghost_white + @color/madison + @color/dusk + @color/curious_blue + @color/cornflower + @color/dusk + + @color/transparent_ghost_white + + @color/slate_grey + + @color/curious_blue + @color/curious_blue + @color/curious_blue + + @color/dark_slate_grey + @color/white_smoke + @color/white_smoke + + @color/ghost_white + @color/navy + @color/royal_blue + + @color/deep_koamaru + @color/metallic_silver + + @color/midnight_blue2 + @color/dusk + + @color/midnight_blue2 + @color/curious_blue + @color/dodger_blue + @color/gull_grayapprox + + @color/curious_blue + @color/curious_blue + @color/midnight_blue2 + @color/curious_blue + @color/geyser + @color/metallic_silver + @color/dusk + + @color/curious_blue + @color/royal_blue + @color/white + @color/curious_blue + @color/geyser + @color/dusk + @color/curious_blue + + @color/ionos_default_text_color + + @color/bg_default + @color/bg_default + @color/transparent + @color/appbar + + @color/ghost_white + @color/curious_blue + @color/slate_grey + @color/slate_grey + @color/ghost_white + @color/dodger_blue + + @color/navy + @color/curious_blue + + + @color/white_smoke + @color/dusk + @color/dusk + + @color/curious_blue + @color/white + + @color/dodger_blue + + @color/curious_blue + @color/white + diff --git a/app/src/gplay/res/values-nl/ionos-strings.xml b/app/src/gplay/res/values-nl/ionos-strings.xml new file mode 100644 index 000000000000..c2ba63152289 --- /dev/null +++ b/app/src/gplay/res/values-nl/ionos-strings.xml @@ -0,0 +1,24 @@ + + + + + + + Welkom bij je cloud storage + Login + Je moet inloggen via je browser + Annuleren + Opnieuw proberen + + + Filteren + + + Opslag in gebruik + + \ No newline at end of file diff --git a/app/src/gplay/res/values-v27/ionos-styles.xml b/app/src/gplay/res/values-v27/ionos-styles.xml new file mode 100644 index 000000000000..5cfc7277d38d --- /dev/null +++ b/app/src/gplay/res/values-v27/ionos-styles.xml @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/app/src/gplay/res/values/ionos-colors.xml b/app/src/gplay/res/values/ionos-colors.xml new file mode 100644 index 000000000000..d4e83191eb4b --- /dev/null +++ b/app/src/gplay/res/values/ionos-colors.xml @@ -0,0 +1,141 @@ + + + + + #003D8F + + @color/ionos_default_text_color + @color/dusk + @color/white + @color/ionos_toolbar_color + @color/ghost_white + @color/white_smoke + @color/ionos_default_icon_color + + @color/bg_default + @color/bg_default + + @color/ghost_white + + @color/ionos_navigation_bar_background + @color/navigation_bar_background_color + @color/ghost_white + @color/lavender_mist + @color/white + + @color/ionos_navigation_bar_background + @color/ghost_white + @color/navy + @color/navy + @color/dusk + @color/dodger_blue + @color/chocolate + + @color/madison + @color/dodger_blue + @color/gull_grayapprox + + @color/white + @color/white + @color/geyser + + @color/transparent + @color/navy + @color/transparent + @color/madison + @color/navy + @color/gull_grayapprox + @color/madison + @color/white + @color/gull_grayapprox + @color/dodger_blue + @color/royal_blue + @color/gull_grayapprox + + @color/lavender_mist + + @color/slate_grey + + @color/geyser + @color/geyser + @color/metallic_silver + @color/white + @color/dodger_blue + @color/dodger_blue + + @color/bg_default + + @color/bg_default + @color/text_color + @color/text_color + @color/geyser + @color/dodger_blue + + @color/ghost_white + @color/dusk + @color/dusk + + @color/midnight_blue2 + @color/ghost_white + @color/cornflower + + @color/geyser + @color/navy + + @color/white + @color/dusk + + @color/white + @color/dodger_blue + @color/royal_blue + @color/gull_grayapprox + + @color/dodger_blue + @color/dodger_blue + @color/ghost_white + @color/dodger_blue + @color/navy + @color/dusk + @color/lavender_mist + + @color/white + @color/lavender_mist + @color/dodger_blue + @color/dodger_blue + @color/geyser + @color/geyser + @color/dodger_blue + + @color/ionos_default_text_color + + @color/navy + @color/dodger_blue + @color/slate_grey + @color/slate_grey + @color/navy + @color/lavender_mist + + @color/lavender_mist + @color/dodger_blue + + + @color/ghost_white + @color/dusk + @color/dusk + + + @color/ghost_white + + @color/dodger_blue + @color/white + + @color/dodger_blue + @color/white + + @color/semitransparent_black + diff --git a/app/src/gplay/res/values/ionos-dimens.xml b/app/src/gplay/res/values/ionos-dimens.xml new file mode 100644 index 000000000000..4635fa1c6c48 --- /dev/null +++ b/app/src/gplay/res/values/ionos-dimens.xml @@ -0,0 +1,89 @@ + + + + + + + 0.2 + 0.4 + 0.8 + + + 8dp + 16dp + 24dp + 8dp + 16dp + 8dp + 8dp + 16dp + 8dp + 16sp + 14sp + + + 40dp + 44dp + 52dp + 32dp + 96dp + 22dp + 6dp + 24dp + 24dp + + 44dp + 64dp + + 8dp + 56dp + 32dp + 4dp + 80dp + + 16sp + 12sp + 24sp + + 24dp + 24dp + 24dp + 48dp + 12dp + 12dp + 16dp + 16dp + 12dp + + 24dp + 12dp + 12dp + 8dp + 12dp + 12dp + + 12dp + + 16dp + + 2dp + + 32dp + 4dp + 5dp + + 1dp + 8dp + 14sp + 16dp + + 1000000dp + + 1 + + \ No newline at end of file diff --git a/app/src/gplay/res/values/ionos-setup.xml b/app/src/gplay/res/values/ionos-setup.xml new file mode 100644 index 000000000000..2c509b18b2fd --- /dev/null +++ b/app/src/gplay/res/values/ionos-setup.xml @@ -0,0 +1,52 @@ + + + + + IONOS HiDrive Next + com.ionos.hidrivenext + com.ionos.hidrivenext.provider + com.ionos.hidrivenext.providers.UsersAndGroupsSearchProvider + com.ionos.hidrivenext.providers.UsersAndGroupsSearchProvider.action.SHARE_WITH + com.ionos.hidrivenext.documents + com.ionos.hidrivenext.files + com.ionos.hidrivenext.providers.imageCache + ionos.db + ionos + ionos + IONOS HiDrive Next + Mozilla/5.0 (Android) IONOS HiDrive Next/%1$s%2$s + false + + + https://easy-qa-1.nextcloud-ionos.com/login/v2 + + + false + + + true + false + false + + + false + true + true + true + true + true + true + https://wl.hidrive.com/easy/0132 + https://wl.hidrive.com/easy/0192 + + https://wl.hidrive.com/easy/0102 + https://wl.hidrive.com/easy/0162 + https://wl.hidrive.com/easy/0182 + + + diff --git a/app/src/gplay/res/values/ionos-strings.xml b/app/src/gplay/res/values/ionos-strings.xml new file mode 100644 index 000000000000..c5655b2d2a4b --- /dev/null +++ b/app/src/gplay/res/values/ionos-strings.xml @@ -0,0 +1,24 @@ + + + + + + + Welcome to the cloud storage + Log In + You need to login over browser + Cancel + Retry + + + Filter + + + Storage used + + \ No newline at end of file diff --git a/app/src/gplay/res/values/ionos-styles.xml b/app/src/gplay/res/values/ionos-styles.xml new file mode 100644 index 000000000000..942f1016cd71 --- /dev/null +++ b/app/src/gplay/res/values/ionos-styles.xml @@ -0,0 +1,296 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/gplay/res/xml/preferences.xml b/app/src/gplay/res/xml/preferences.xml new file mode 100644 index 000000000000..5da56f4948de --- /dev/null +++ b/app/src/gplay/res/xml/preferences.xml @@ -0,0 +1,191 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/ionos/analycis/AnalyticsManager.kt b/app/src/main/java/com/ionos/analycis/AnalyticsManager.kt new file mode 100644 index 000000000000..ee6693048712 --- /dev/null +++ b/app/src/main/java/com/ionos/analycis/AnalyticsManager.kt @@ -0,0 +1,12 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.ionos.analycis + +interface AnalyticsManager { + fun setEnabled(enabled: Boolean) +} diff --git a/app/src/main/java/com/ionos/analycis/FirebaseAnalyticsManager.kt b/app/src/main/java/com/ionos/analycis/FirebaseAnalyticsManager.kt new file mode 100644 index 000000000000..7c65885dceb1 --- /dev/null +++ b/app/src/main/java/com/ionos/analycis/FirebaseAnalyticsManager.kt @@ -0,0 +1,23 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.ionos.analycis + +import android.content.Context +import com.google.firebase.analytics.FirebaseAnalytics +import javax.inject.Inject + +class FirebaseAnalyticsManager @Inject constructor( + private val context: Context, +) : AnalyticsManager { + + private val firebaseAnalytics by lazy { FirebaseAnalytics.getInstance(context) } + + override fun setEnabled(enabled: Boolean) { + firebaseAnalytics.setAnalyticsCollectionEnabled(enabled) + } +} diff --git a/app/src/main/java/com/ionos/annotation/IonosCustomization.kt b/app/src/main/java/com/ionos/annotation/IonosCustomization.kt new file mode 100644 index 000000000000..9b7730a1021c --- /dev/null +++ b/app/src/main/java/com/ionos/annotation/IonosCustomization.kt @@ -0,0 +1,24 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.ionos.annotation + +@Target( + AnnotationTarget.FUNCTION, + AnnotationTarget.FIELD, + AnnotationTarget.CLASS, + AnnotationTarget.PROPERTY, + AnnotationTarget.CONSTRUCTOR, + AnnotationTarget.EXPRESSION, +) +@Retention(AnnotationRetention.SOURCE) +/* Used to highlight changes in core code +* Alternatives: +* - for layouts use - 'app:ionosCustomization=""' +* - comment with text '' where other options is not applicable +* */ +annotation class IonosCustomization(val value: String = "") \ No newline at end of file diff --git a/app/src/main/java/com/ionos/authorization_method/AuthorizationMethodActivity.kt b/app/src/main/java/com/ionos/authorization_method/AuthorizationMethodActivity.kt new file mode 100644 index 000000000000..732965836922 --- /dev/null +++ b/app/src/main/java/com/ionos/authorization_method/AuthorizationMethodActivity.kt @@ -0,0 +1,43 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.ionos.authorization_method + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import com.owncloud.android.authentication.AuthenticatorActivity +import com.owncloud.android.databinding.ViewAuthorizationMethodBinding + +class AuthorizationMethodActivity : AppCompatActivity() { + + companion object { + @JvmStatic + fun createInstance(context: Context) = Intent(context, AuthorizationMethodActivity::class.java) + } + + private val viewBinding by lazy { ViewAuthorizationMethodBinding.inflate(layoutInflater) } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(viewBinding.root) + + initListeners() + } + + private fun initListeners() { + viewBinding.bLogin.setOnClickListener { login() } + } + + private fun login() { + val intent = Intent(this, AuthenticatorActivity::class.java).apply { + putExtra(AuthenticatorActivity.EXTRA_USE_PROVIDER_AS_WEBLOGIN, true) + } + startActivity(intent); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ionos/di/StratoModule.kt b/app/src/main/java/com/ionos/di/StratoModule.kt new file mode 100644 index 000000000000..a981b87a2ba2 --- /dev/null +++ b/app/src/main/java/com/ionos/di/StratoModule.kt @@ -0,0 +1,34 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.ionos.di + +import com.ionos.analycis.AnalyticsManager +import com.ionos.privacy.DataProtectionActivity +import com.ionos.analycis.FirebaseAnalyticsManager +import com.ionos.privacy.PrivacySettingsActivity +import com.ionos.scanbot.di.NCScanbotModule +import com.ionos.startup.IonosInitializer +import dagger.Binds +import dagger.Module +import dagger.android.ContributesAndroidInjector + +@Module(includes = [NCScanbotModule::class]) +abstract class StratoModule { + + @ContributesAndroidInjector + abstract fun ionosInitializer(): IonosInitializer + + @ContributesAndroidInjector + abstract fun dataProtectionActivity(): DataProtectionActivity + + @ContributesAndroidInjector + abstract fun privacySettingsActivity(): PrivacySettingsActivity + + @Binds + abstract fun analyticsManager(firebaseAnalyticsManager: FirebaseAnalyticsManager): AnalyticsManager +} diff --git a/app/src/main/java/com/ionos/privacy/DataProtectionActivity.kt b/app/src/main/java/com/ionos/privacy/DataProtectionActivity.kt new file mode 100644 index 000000000000..4a94d428d381 --- /dev/null +++ b/app/src/main/java/com/ionos/privacy/DataProtectionActivity.kt @@ -0,0 +1,147 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.ionos.privacy + +import android.content.Context +import android.content.Intent +import android.graphics.Color +import android.os.Bundle +import android.text.method.LinkMovementMethod +import androidx.activity.SystemBarStyle +import androidx.activity.addCallback +import androidx.activity.enableEdgeToEdge +import androidx.activity.viewModels +import androidx.appcompat.app.AppCompatDelegate +import androidx.core.content.ContextCompat +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.WindowInsetsControllerCompat +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope +import com.ionos.utils.context.isDarkMode +import com.ionos.utils.text.convertAnnotatedTextToLinks +import com.nextcloud.utils.extensions.getParcelableArgument +import com.owncloud.android.R +import com.owncloud.android.databinding.ActivityDataProtectionBinding +import com.owncloud.android.ui.activity.BaseActivity +import com.owncloud.android.ui.activity.ExternalSiteWebView +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import javax.inject.Inject + +class DataProtectionActivity : BaseActivity() { + + @Inject + lateinit var viewModelFactory: DataProtectionViewModel.Factory + + private val viewModel by viewModels { viewModelFactory } + + private val binding by lazy { ActivityDataProtectionBinding.inflate(layoutInflater) } + + private val detailPageOnBackPressedCallback by lazy { + onBackPressedDispatcher.addCallback(this) { viewModel.onDetailPageBackButtonClick() } + } + + override fun onCreate(savedInstanceState: Bundle?) { + enableEdgeToEdge(SystemBarStyle.dark(Color.TRANSPARENT), SystemBarStyle.dark(Color.TRANSPARENT)) + super.onCreate(savedInstanceState) + setContentView(binding.root) + + val descriptionText = getText(R.string.ionos_data_protection_description).convertAnnotatedTextToLinks( + linkColor = ContextCompat.getColor(this, R.color.curious_blue), + linkUnderline = false, + linkHandler = ::handleLink, + ) + + binding.overviewPage.descriptionTextView.text = descriptionText + binding.overviewPage.descriptionTextView.movementMethod = LinkMovementMethod.getInstance() + + binding.overviewPage.agreeButton.setOnClickListener { viewModel.onAgreeButtonClick() } + binding.overviewPage.settingsButton.setOnClickListener { viewModel.onSettingsButtonClick() } + binding.detailPage.toolbar.setNavigationOnClickListener { viewModel.onDetailPageBackButtonClick() } + binding.detailPage.saveButton.setOnClickListener { viewModel.onSaveButtonClick() } + binding.detailPage.switchers.analyticsSwitch.setOnCheckedChangeListener { _, isChecked -> + viewModel.onAnalyticsCheckedChange(isChecked) + } + + ViewCompat.setOnApplyWindowInsetsListener(binding.root) { rootView, windowInsets -> + val insetsType = WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() + val insets = windowInsets.getInsets(insetsType) + binding.overviewPage.root.setPadding(insets.left, insets.top, insets.right, insets.bottom) + binding.detailPage.root.setPadding(insets.left, insets.top, insets.right, insets.bottom) + ViewCompat.onApplyWindowInsets(rootView, windowInsets) + } + + viewModel.stateFlow + .flowWithLifecycle(lifecycle) + .onEach(::updateState) + .launchIn(lifecycleScope) + } + + private fun handleLink(type: String) { + when (type) { + INFORMATION_LINK -> openPrivacyPolicyScreen() + REJECT_LINK -> viewModel.onRejectLinkClick() + else -> throw IllegalArgumentException("Unknown link type: $type") + } + } + + private fun openPrivacyPolicyScreen() { + val externalWebViewIntent = Intent(this, ExternalSiteWebView::class.java) + externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_TITLE, getString(R.string.privacy)) + externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_URL, getString(R.string.privacy_url)) + externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, false) + externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_MENU_ITEM_ID, -1) + startActivity(externalWebViewIntent) + } + + private fun updateState(state: DataProtectionViewModel.State) { + if (binding.viewSwitcher.displayedChild != state.page.index) { + binding.viewSwitcher.displayedChild = state.page.index + } + if (state.page == DataProtectionViewModel.Page.OVERVIEW) { + delegate.localNightMode = AppCompatDelegate.MODE_NIGHT_YES + setSystemBarsAppearance(false) + detailPageOnBackPressedCallback.isEnabled = false + } else { + delegate.localNightMode = AppCompatDelegate.MODE_NIGHT_UNSPECIFIED + setSystemBarsAppearance(!isDarkMode()) + detailPageOnBackPressedCallback.isEnabled = true + } + if (binding.detailPage.switchers.analyticsSwitch.isChecked != state.isAnalyticsEnabled) { + binding.detailPage.switchers.analyticsSwitch.isChecked = state.isAnalyticsEnabled + } + if (state.isProcessed) { + intent.getParcelableArgument(TARGET_SCREEN_INTENT_KEY, Intent::class.java)?.let(::startActivity) + finish() + } + } + + private fun setSystemBarsAppearance(isLight: Boolean) { + val controller = WindowInsetsControllerCompat(window, window.decorView) + controller.isAppearanceLightStatusBars = isLight + controller.isAppearanceLightNavigationBars = isLight + } + + companion object { + private const val TARGET_SCREEN_INTENT_KEY = "target_screen_intent" + private const val INFORMATION_LINK = "information_link" + private const val REJECT_LINK = "reject_link" + + @JvmStatic + fun createIntent(context: Context): Intent { + return Intent(context, DataProtectionActivity::class.java) + } + + @JvmStatic + fun createIntent(context: Context, targetScreenIntent: Intent): Intent { + return Intent(context, DataProtectionActivity::class.java) + .putExtra(TARGET_SCREEN_INTENT_KEY, targetScreenIntent) + } + } +} diff --git a/app/src/main/java/com/ionos/privacy/DataProtectionViewModel.kt b/app/src/main/java/com/ionos/privacy/DataProtectionViewModel.kt new file mode 100644 index 000000000000..281561bbd54a --- /dev/null +++ b/app/src/main/java/com/ionos/privacy/DataProtectionViewModel.kt @@ -0,0 +1,81 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.ionos.privacy + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.ionos.analycis.AnalyticsManager +import com.nextcloud.client.account.UserAccountManager +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import javax.inject.Inject +import javax.inject.Provider + +class DataProtectionViewModel @Inject constructor( + private val analyticsManager: AnalyticsManager, + private val privacyPreferences: PrivacyPreferences, + private val userAccountManager: UserAccountManager, +) : ViewModel() { + + private var _stateFlow = MutableStateFlow(createInitialState()) + val stateFlow = _stateFlow.asStateFlow() + + fun onAgreeButtonClick() { + save(isAnalyticsEnabled = true) + } + + fun onRejectLinkClick() { + save(isAnalyticsEnabled = false) + } + + fun onSettingsButtonClick() { + _stateFlow.update { it.copy(page = Page.DETAIL) } + } + + fun onDetailPageBackButtonClick() { + _stateFlow.update { it.copy(page = Page.OVERVIEW) } + } + + fun onAnalyticsCheckedChange(isChecked: Boolean) { + _stateFlow.update { it.copy(isAnalyticsEnabled = isChecked) } + } + + fun onSaveButtonClick() { + save(stateFlow.value.isAnalyticsEnabled) + } + + private fun save(isAnalyticsEnabled: Boolean) { + analyticsManager.setEnabled(isAnalyticsEnabled) + privacyPreferences.setAnalyticsEnabled(isAnalyticsEnabled) + privacyPreferences.setDataProtectionProcessed(userAccountManager.currentOwnCloudAccount?.name) + _stateFlow.update { it.copy(isProcessed = true) } + } + + enum class Page(val index: Int) { + OVERVIEW(0), + DETAIL(1), + } + + private fun createInitialState(): State = State(isAnalyticsEnabled = privacyPreferences.isAnalyticsEnabled()) + + data class State( + val page: Page = Page.OVERVIEW, + val isAnalyticsEnabled: Boolean = false, + val isProcessed: Boolean = false, + ) + + class Factory @Inject constructor( + private val viewModelProvider: Provider, + ) : ViewModelProvider.Factory { + + override fun create(modelClass: Class): T { + return viewModelProvider.get() as T + } + } +} diff --git a/app/src/main/java/com/ionos/privacy/PrivacyPreferences.kt b/app/src/main/java/com/ionos/privacy/PrivacyPreferences.kt new file mode 100644 index 000000000000..1dc35f7c7567 --- /dev/null +++ b/app/src/main/java/com/ionos/privacy/PrivacyPreferences.kt @@ -0,0 +1,59 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.ionos.privacy + +import android.content.Context +import javax.inject.Inject + +class PrivacyPreferences @Inject constructor( + private val context: Context, +) { + private val sharedPreferences by lazy { context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE) } + + fun isDataProtectionProcessed(accountName: String?): Boolean = + getAccountsWithProcessedDataProtection().contains(accountName) + + fun setDataProtectionProcessed(accountName: String?) { + accountName?.let { + sharedPreferences + .edit() + .putStringSet(DATA_PROTECTION_PROCESSED_KEY, getAccountsWithProcessedDataProtection() + accountName) + .apply() + } + } + + fun removeDataProtectionProcessed(accountName: String) { + mutableSetOf(*getAccountsWithProcessedDataProtection().toTypedArray()) + .apply { remove(accountName) } + .let { accountsWithProcessedDataProtection -> + sharedPreferences + .edit() + .putStringSet(DATA_PROTECTION_PROCESSED_KEY, accountsWithProcessedDataProtection) + .apply() + } + } + + private fun getAccountsWithProcessedDataProtection(): Set = + sharedPreferences + .getStringSet(DATA_PROTECTION_PROCESSED_KEY, emptySet()) + ?: emptySet() + + fun isAnalyticsEnabled(): Boolean { + return sharedPreferences.getBoolean(ANALYTICS_ENABLED_KEY, false) + } + + fun setAnalyticsEnabled(value: Boolean) { + sharedPreferences.edit().putBoolean(ANALYTICS_ENABLED_KEY, value).apply() + } + + companion object { + private const val FILE_NAME = "privacy_preferences" + private const val DATA_PROTECTION_PROCESSED_KEY = "data_protection_processed" + private const val ANALYTICS_ENABLED_KEY = "analytics_enabled" + } +} diff --git a/app/src/main/java/com/ionos/privacy/PrivacySettingsActivity.kt b/app/src/main/java/com/ionos/privacy/PrivacySettingsActivity.kt new file mode 100644 index 000000000000..5af89da3419b --- /dev/null +++ b/app/src/main/java/com/ionos/privacy/PrivacySettingsActivity.kt @@ -0,0 +1,70 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.ionos.privacy + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.activity.viewModels +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope +import com.owncloud.android.databinding.ActivityPrivacySettingsBinding +import com.owncloud.android.ui.activity.BaseActivity +import com.owncloud.android.utils.theme.ViewThemeUtils +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import javax.inject.Inject + +class PrivacySettingsActivity : BaseActivity() { + + @Inject + lateinit var viewModelFactory: PrivacySettingsViewModel.Factory + + @Inject + lateinit var viewThemeUtils: ViewThemeUtils + + private val viewModel by viewModels { viewModelFactory } + + private val binding by lazy { ActivityPrivacySettingsBinding.inflate(layoutInflater) } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(binding.root) + + viewThemeUtils.ionos.platform.themeSystemBars(this); + + binding.toolbar.setNavigationOnClickListener { onBackPressedDispatcher.onBackPressed() } + binding.switchers.analyticsSwitch.setOnCheckedChangeListener { _, isChecked -> + viewModel.onAnalyticsCheckedChange(isChecked) + } + + viewModel.stateFlow + .flowWithLifecycle(lifecycle) + .onEach(::updateState) + .launchIn(lifecycleScope) + } + + override fun onStart() { + super.onStart() + viewModel.onStart() + } + + private fun updateState(state: PrivacySettingsViewModel.State) { + if (binding.switchers.analyticsSwitch.isChecked != state.isAnalyticsEnabled) { + binding.switchers.analyticsSwitch.isChecked = state.isAnalyticsEnabled + } + } + + companion object { + + @JvmStatic + fun createIntent(context: Context): Intent { + return Intent(context, PrivacySettingsActivity::class.java) + } + } +} diff --git a/app/src/main/java/com/ionos/privacy/PrivacySettingsViewModel.kt b/app/src/main/java/com/ionos/privacy/PrivacySettingsViewModel.kt new file mode 100644 index 000000000000..c2c5606bcaf0 --- /dev/null +++ b/app/src/main/java/com/ionos/privacy/PrivacySettingsViewModel.kt @@ -0,0 +1,50 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.ionos.privacy + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import com.ionos.analycis.AnalyticsManager +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update +import javax.inject.Inject +import javax.inject.Provider + +class PrivacySettingsViewModel @Inject constructor( + private val analyticsManager: AnalyticsManager, + private val privacyPreferences: PrivacyPreferences, +) : ViewModel() { + + private var _stateFlow = MutableStateFlow(State()) + val stateFlow = _stateFlow.asStateFlow() + + fun onStart() { + val isAnalyticsEnabled = privacyPreferences.isAnalyticsEnabled() + _stateFlow.update { it.copy(isAnalyticsEnabled = isAnalyticsEnabled) } + } + + fun onAnalyticsCheckedChange(isChecked: Boolean) { + _stateFlow.update { it.copy(isAnalyticsEnabled = isChecked) } + analyticsManager.setEnabled(isChecked) + privacyPreferences.setAnalyticsEnabled(isChecked) + } + + data class State( + val isAnalyticsEnabled: Boolean = false, + ) + + class Factory @Inject constructor( + private val viewModelProvider: Provider, + ) : ViewModelProvider.Factory { + + override fun create(modelClass: Class): T { + return viewModelProvider.get() as T + } + } +} diff --git a/app/src/main/java/com/ionos/scanbot/di/NCScanbotModule.kt b/app/src/main/java/com/ionos/scanbot/di/NCScanbotModule.kt new file mode 100644 index 000000000000..9bd8d3cafe6d --- /dev/null +++ b/app/src/main/java/com/ionos/scanbot/di/NCScanbotModule.kt @@ -0,0 +1,46 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.ionos.scanbot.di + +import com.ionos.scanbot.image_loader.ImageLoader +import com.ionos.scanbot.image_loading.ImageLoaderImpl +import com.ionos.scanbot.license.LoadScanbotLicense +import com.ionos.scanbot.license.LoadScanbotLicenseImpl +import com.ionos.scanbot.logger.ScanbotLogger +import com.ionos.scanbot.screens.save.SelectDirectoryContract +import com.ionos.scanbot.upload.ScanbotUploader +import com.ionos.scanbot.upload.SelectDirectoryContractImpl +import com.ionos.scanbot.upload.use_case.Uploader +import com.ionos.scanbot.util.GetLocalFreeSpace +import com.ionos.scanbot.util.logger.Logger +import com.ionos.scanbot.utils.GetLocalFreeSpaceImpl +import dagger.Binds +import dagger.Module + +@Module(includes = [ScanbotModule::class]) +abstract class NCScanbotModule { + + @Binds + abstract fun bindLoadLicense(loadScanbotLicense: LoadScanbotLicenseImpl): LoadScanbotLicense + + @Binds + abstract fun bindImageLoader(imageLoader: ImageLoaderImpl): ImageLoader + + @Binds + abstract fun bindUploader(uploader: ScanbotUploader): Uploader + + @Binds + abstract fun bindLogger(logger: ScanbotLogger): Logger + + @Binds + abstract fun bindSelectDirectoryContract(contract: SelectDirectoryContractImpl): SelectDirectoryContract + + @Binds + abstract fun bindGetLocalFreeSpaceImpl(getLocalFreeSpaceImpl: GetLocalFreeSpaceImpl): GetLocalFreeSpace + +} \ No newline at end of file diff --git a/app/src/main/java/com/ionos/scanbot/image_loading/ImageLoaderImpl.kt b/app/src/main/java/com/ionos/scanbot/image_loading/ImageLoaderImpl.kt new file mode 100644 index 000000000000..59a932a196b4 --- /dev/null +++ b/app/src/main/java/com/ionos/scanbot/image_loading/ImageLoaderImpl.kt @@ -0,0 +1,22 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.ionos.scanbot.image_loading + +import com.ionos.scanbot.image_loader.ImageLoader +import com.ionos.scanbot.image_loader.ImageRequestBuilder +import java.io.File +import javax.inject.Inject + +class ImageLoaderImpl @Inject constructor( +) : ImageLoader { + + override fun load(file: File): ImageRequestBuilder { + return ImageRequestBuilderImpl(file) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/ionos/scanbot/image_loading/ImageRequestBuilderImpl.kt b/app/src/main/java/com/ionos/scanbot/image_loading/ImageRequestBuilderImpl.kt new file mode 100644 index 000000000000..26b345f12574 --- /dev/null +++ b/app/src/main/java/com/ionos/scanbot/image_loading/ImageRequestBuilderImpl.kt @@ -0,0 +1,41 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.ionos.scanbot.image_loading + +import android.widget.ImageView +import com.bumptech.glide.Glide +import com.ionos.scanbot.image_loader.ImageLoaderOptions +import com.ionos.scanbot.image_loader.ImageRequestBuilder +import com.ionos.scanbot.image_loader.ScaleType +import java.io.File + +class ImageRequestBuilderImpl( + private val file: File, +) : ImageRequestBuilder { + + private var options: ImageLoaderOptions? = null + + override fun options(options: ImageLoaderOptions): ImageRequestBuilder { + this.options = options + return this + } + + override fun into(target: ImageView) { + Glide.with(target.context) + .load(file) + .run { + when (options?.scaleType) { + ScaleType.CENTER_CROP -> centerCrop() + ScaleType.CENTER_INSIDE -> fitCenter() + else -> this + } + } + .signature(ObjectKey(file.lastModified())) + .into(target) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ionos/scanbot/image_loading/ObjectKey.java b/app/src/main/java/com/ionos/scanbot/image_loading/ObjectKey.java new file mode 100644 index 000000000000..de9b1fec7535 --- /dev/null +++ b/app/src/main/java/com/ionos/scanbot/image_loading/ObjectKey.java @@ -0,0 +1,49 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.ionos.scanbot.image_loading; + +import com.bumptech.glide.load.Key; + +import java.nio.charset.Charset; +import java.security.MessageDigest; + +import androidx.annotation.NonNull; + +public final class ObjectKey implements Key { + private static final Charset CHARSET = Charset.forName(STRING_CHARSET_NAME); + + private final Object object; + + public ObjectKey(@NonNull Object object) { + this.object = object; + } + + @Override + public String toString() { + return "ObjectKey{" + "object=" + object + '}'; + } + + @Override + public boolean equals(Object o) { + if (o instanceof ObjectKey) { + ObjectKey other = (ObjectKey) o; + return object.equals(other.object); + } + return false; + } + + @Override + public int hashCode() { + return object.hashCode(); + } + + @Override + public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) { + messageDigest.update(object.toString().getBytes(CHARSET)); + } +} diff --git a/app/src/main/java/com/ionos/scanbot/license/DownloadLicenseRemoteOperation.kt b/app/src/main/java/com/ionos/scanbot/license/DownloadLicenseRemoteOperation.kt new file mode 100644 index 000000000000..3f7e8dc70476 --- /dev/null +++ b/app/src/main/java/com/ionos/scanbot/license/DownloadLicenseRemoteOperation.kt @@ -0,0 +1,59 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.ionos.scanbot.license + +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import org.apache.commons.httpclient.methods.GetMethod + +class DownloadLicenseRemoteOperation( + private val licenseKeyUrl: String, +) : RemoteOperation() { + + var license: String? = null + private set + + override fun run(client: OwnCloudClient?): RemoteOperationResult { + return try { + if (client == null) throw IllegalArgumentException("Client should not be null") + + val getMethod = GetMethod(licenseKeyUrl) + + license = downloadFile(client, getMethod) + RemoteOperationResult(true, getMethod) + } catch (e: Exception) { + RemoteOperationResult(e) + } + } + + private fun downloadFile( + client: OwnCloudClient, + getMethod: GetMethod, + ): String? { + try { + val status = client.executeMethod(getMethod) + if (isSuccess(status)) { + val bytes = getMethod.responseBodyAsStream + .use { + it.readBytes() + } + + return LicenseResponseTransformer().transform(bytes) + } + } finally { + getMethod.releaseConnection() + } + + return null + } + + private fun isSuccess(status: Int): Boolean { + return status == 200 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ionos/scanbot/license/LicenseResponseTransformer.kt b/app/src/main/java/com/ionos/scanbot/license/LicenseResponseTransformer.kt new file mode 100644 index 000000000000..f262e46872bf --- /dev/null +++ b/app/src/main/java/com/ionos/scanbot/license/LicenseResponseTransformer.kt @@ -0,0 +1,43 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.ionos.scanbot.license + +/** + * User: Dima Muravyov + * Date: 06.02.2020 + */ +internal class LicenseResponseTransformer { + + companion object { + private const val SEPARATOR = "\"" + private const val ESCAPED_NEW_LINE = "\\n" + private const val NEW_LINE = "\n" + } + + fun transform(bytes: ByteArray): String? { + val inlinedResponse = String(bytes) + + var scanbotLicenseKey = "" + var startIndex = inlinedResponse.indexOf(SEPARATOR) + while (startIndex != -1) { + val endIndex = inlinedResponse.indexOf(SEPARATOR, startIndex + 1) + if (endIndex == -1) { + scanbotLicenseKey = "" + break + } + + scanbotLicenseKey += inlinedResponse.substring(startIndex + 1, endIndex) + + startIndex = inlinedResponse.indexOf(SEPARATOR, endIndex + 1) + } + + scanbotLicenseKey = scanbotLicenseKey.replace(ESCAPED_NEW_LINE, NEW_LINE) + + return if (scanbotLicenseKey.isNotBlank()) scanbotLicenseKey else null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ionos/scanbot/license/LoadScanbotLicenseImpl.kt b/app/src/main/java/com/ionos/scanbot/license/LoadScanbotLicenseImpl.kt new file mode 100644 index 000000000000..f36783fe41c9 --- /dev/null +++ b/app/src/main/java/com/ionos/scanbot/license/LoadScanbotLicenseImpl.kt @@ -0,0 +1,42 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.ionos.scanbot.license + +import android.content.Context +import androidx.work.Constraints +import androidx.work.ExistingWorkPolicy +import androidx.work.NetworkType +import androidx.work.OneTimeWorkRequest +import androidx.work.OutOfQuotaPolicy +import androidx.work.WorkManager +import javax.inject.Inject + +class LoadScanbotLicenseImpl @Inject constructor( + context: Context, +) : LoadScanbotLicense { + + private val workManager by lazy { WorkManager.getInstance(context) } + + override fun invoke() { + val request = + OneTimeWorkRequest.Builder(ScanbotLicenseDownloadWorker::class.java) + .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) + .setConstraints( + Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build() + ) + .build() + + workManager.enqueueUniqueWork( + ScanbotLicenseDownloadWorker.SCANBOT_LICENSE_DOWNLOAD_WORKER, + ExistingWorkPolicy.KEEP, + request + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ionos/scanbot/license/ScanbotLicenseDownloadWorker.kt b/app/src/main/java/com/ionos/scanbot/license/ScanbotLicenseDownloadWorker.kt new file mode 100644 index 000000000000..ca85c02f274c --- /dev/null +++ b/app/src/main/java/com/ionos/scanbot/license/ScanbotLicenseDownloadWorker.kt @@ -0,0 +1,86 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.ionos.scanbot.license + +import android.content.Context +import android.content.pm.ServiceInfo +import android.os.Build +import androidx.work.ForegroundInfo +import androidx.work.Worker +import androidx.work.WorkerParameters +import com.ionos.scanbot.initializer.TryToInitScanbotSdk +import com.nextcloud.client.account.UserAccountManager +import com.nextcloud.client.jobs.download.DownloadNotificationManager +import com.owncloud.android.R +import com.owncloud.android.lib.common.OwnCloudClientManagerFactory +import com.owncloud.android.utils.theme.ViewThemeUtils +import java.security.SecureRandom + +class ScanbotLicenseDownloadWorker( + licenseUrl: String, + viewThemeUtils: ViewThemeUtils, + private val accountManager: UserAccountManager, + private val licenseKeyStore: LicenseKeyStore, + private val tryToInitScanbotSdk: TryToInitScanbotSdk, + private val context: Context, + params: WorkerParameters, +): Worker(context, params){ + + companion object { + const val SCANBOT_LICENSE_DOWNLOAD_WORKER = "SCANBOT_LICENSE_DOWNLOAD_WORKER" + } + + private val operation = DownloadLicenseRemoteOperation(licenseUrl) + private var notificationManager = DownloadNotificationManager( + SecureRandom().nextInt(), + context, + viewThemeUtils + ) + + override fun doWork(): Result { + return try { + val ocAccount = accountManager.user.toOwnCloudAccount() + val downloadClient = + OwnCloudClientManagerFactory.getDefaultSingleton().getClientFor(ocAccount, context) + + val result = operation.execute(downloadClient) + + if (result.isSuccess) { + operation.license?.let { + licenseKeyStore.saveLicenseKey(it) + tryToInitScanbotSdk(it) + } + Result.success() + } + else Result.retry() + }catch (e: Exception){ + Result.failure() + } + } + + override fun getForegroundInfo(): ForegroundInfo { + notificationManager.notificationBuilder.run { + setProgress(100, 0, true) + setContentTitle(context.getString(R.string.downloader_download_in_progress_ticker)) + } + + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){ + ForegroundInfo( + notificationManager.getId(), + notificationManager.getNotification(), + ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC + ) + } else{ + ForegroundInfo( + notificationManager.getId(), + notificationManager.getNotification(), + ) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/ionos/scanbot/license/ScanbotLicenseJobFactory.kt b/app/src/main/java/com/ionos/scanbot/license/ScanbotLicenseJobFactory.kt new file mode 100644 index 000000000000..b5ddc52b7892 --- /dev/null +++ b/app/src/main/java/com/ionos/scanbot/license/ScanbotLicenseJobFactory.kt @@ -0,0 +1,39 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.ionos.scanbot.license + +import android.content.Context +import androidx.work.WorkerParameters +import com.ionos.scanbot.di.qualifiers.ScanbotLicenseKeyUrl +import com.ionos.scanbot.initializer.TryToInitScanbotSdk +import com.nextcloud.client.account.UserAccountManager +import com.owncloud.android.utils.theme.ViewThemeUtils +import javax.inject.Inject +import javax.inject.Provider + +class ScanbotLicenseJobFactory @Inject constructor( + @ScanbotLicenseKeyUrl private val scanbotLicenseUrl: String, + private val accountManager: UserAccountManager, + private val licenseKeyStore: LicenseKeyStore, + private val tryToInitScanbotSdk: TryToInitScanbotSdk, + private val viewThemeUtils: Provider, +) { + + fun create(context: Context, params: WorkerParameters): ScanbotLicenseDownloadWorker { + return ScanbotLicenseDownloadWorker( + scanbotLicenseUrl, + viewThemeUtils.get(), + accountManager, + licenseKeyStore, + tryToInitScanbotSdk, + context, + params + ) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/ionos/scanbot/logger/ScanbotLogger.kt b/app/src/main/java/com/ionos/scanbot/logger/ScanbotLogger.kt new file mode 100644 index 000000000000..46c7a67f2de4 --- /dev/null +++ b/app/src/main/java/com/ionos/scanbot/logger/ScanbotLogger.kt @@ -0,0 +1,28 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.ionos.scanbot.logger + +import com.ionos.scanbot.util.logger.Logger +import javax.inject.Inject +import com.nextcloud.client.logger.Logger as NextCloudLogger + +class ScanbotLogger @Inject constructor( + private val nextCloudLogger: NextCloudLogger +) : Logger { + + companion object { + private const val GLOBAL_TAG = "ScanbotModule" + } + + override fun logE(message: String, t: Throwable?) { + t?.let { + nextCloudLogger.e(GLOBAL_TAG, message, it) + } ?: nextCloudLogger.e(GLOBAL_TAG, message) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/ionos/scanbot/upload/ScanbotUploader.kt b/app/src/main/java/com/ionos/scanbot/upload/ScanbotUploader.kt new file mode 100644 index 000000000000..6ddebd69a5a1 --- /dev/null +++ b/app/src/main/java/com/ionos/scanbot/upload/ScanbotUploader.kt @@ -0,0 +1,39 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.ionos.scanbot.upload + +import com.ionos.scanbot.upload.use_case.Uploader +import com.nextcloud.client.account.CurrentAccountProvider +import com.nextcloud.client.jobs.upload.FileUploadHelper +import com.nextcloud.client.jobs.upload.FileUploadWorker +import com.owncloud.android.files.services.NameCollisionPolicy +import com.owncloud.android.operations.UploadFileOperation +import java.io.File +import javax.inject.Inject + +class ScanbotUploader @Inject constructor( + private val currentAccountProvider: CurrentAccountProvider +): Uploader { + override fun upload(uploadFolder: String, pageList: List) { + val uploadPaths = pageList.map { + File(uploadFolder, File(it).name).path + }.toTypedArray() + + FileUploadHelper.instance().uploadNewFiles( + currentAccountProvider.user, + pageList.toTypedArray(), + uploadPaths, + FileUploadWorker.LOCAL_BEHAVIOUR_DELETE, + true, + UploadFileOperation.CREATED_BY_USER, + false, + false, + NameCollisionPolicy.RENAME + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ionos/scanbot/upload/SelectDirectoryContractImpl.kt b/app/src/main/java/com/ionos/scanbot/upload/SelectDirectoryContractImpl.kt new file mode 100644 index 000000000000..9ea88c830d98 --- /dev/null +++ b/app/src/main/java/com/ionos/scanbot/upload/SelectDirectoryContractImpl.kt @@ -0,0 +1,42 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.ionos.scanbot.upload + +import android.app.Activity +import android.content.Context +import android.content.Intent +import com.ionos.scanbot.screens.save.SelectDirectoryContract +import com.ionos.scanbot.upload.target_provider.ScanbotUploadTarget +import com.nextcloud.utils.extensions.getParcelableArgument +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.ui.activity.FolderPickerActivity +import javax.inject.Inject + +class SelectDirectoryContractImpl @Inject constructor( +) : SelectDirectoryContract() { + + override fun createIntent(context: Context, input: Unit): Intent { + return Intent(context, FolderPickerActivity::class.java) + .apply { + putExtra(FolderPickerActivity.EXTRA_ACTION, FolderPickerActivity.CHOOSE_LOCATION) + } + } + + override fun parseResult(resultCode: Int, intent: Intent?): SelectDirectoryResult { + if (resultCode != Activity.RESULT_OK || intent == null || FolderPickerActivity.EXTRA_FOLDER == null) + return SelectDirectoryResult.Canceled + + return intent.getParcelableArgument(FolderPickerActivity.EXTRA_FOLDER, OCFile::class.java) + ?.remotePath?.let { + SelectDirectoryResult.Success( + ScanbotUploadTarget(it) + ) + } ?: SelectDirectoryResult.Canceled + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/ionos/scanbot/utils/GetLocalFreeSpaceImpl.kt b/app/src/main/java/com/ionos/scanbot/utils/GetLocalFreeSpaceImpl.kt new file mode 100644 index 000000000000..e5537e9859b0 --- /dev/null +++ b/app/src/main/java/com/ionos/scanbot/utils/GetLocalFreeSpaceImpl.kt @@ -0,0 +1,16 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.ionos.scanbot.utils + +import com.ionos.scanbot.util.GetLocalFreeSpace +import com.owncloud.android.utils.FileStorageUtils +import javax.inject.Inject + +class GetLocalFreeSpaceImpl @Inject constructor() : GetLocalFreeSpace { + override fun invoke(): Long = FileStorageUtils.getUsableSpace() +} \ No newline at end of file diff --git a/app/src/main/java/com/ionos/startup/IonosInitializer.kt b/app/src/main/java/com/ionos/startup/IonosInitializer.kt new file mode 100644 index 000000000000..27ba4e9eaf54 --- /dev/null +++ b/app/src/main/java/com/ionos/startup/IonosInitializer.kt @@ -0,0 +1,38 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO AG. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.ionos.startup + +import android.content.Context +import androidx.startup.Initializer +import com.ionos.analycis.AnalyticsManager +import com.ionos.privacy.PrivacyPreferences +import com.ionos.scanbot.initializer.ScanbotInitializer +import com.owncloud.android.MainApp +import javax.inject.Inject + +class IonosInitializer : Initializer { + + @Inject + lateinit var privacyPreferences: PrivacyPreferences + + @Inject + lateinit var analyticsManager: AnalyticsManager + + @Inject + lateinit var scanbotInitializer: ScanbotInitializer + + override fun create(context: Context) { + (context.applicationContext as MainApp).androidInjector().inject(this) + scanbotInitializer.initialize() + analyticsManager.setEnabled(privacyPreferences.isAnalyticsEnabled()) + } + + override fun dependencies(): List>> { + return emptyList() + } +} diff --git a/app/src/main/java/com/ionos/utils/IonosAndroidViewThemeUtils.kt b/app/src/main/java/com/ionos/utils/IonosAndroidViewThemeUtils.kt new file mode 100644 index 000000000000..42a327406135 --- /dev/null +++ b/app/src/main/java/com/ionos/utils/IonosAndroidViewThemeUtils.kt @@ -0,0 +1,43 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.ionos.utils + +import android.app.Activity +import android.content.Context +import android.view.View +import android.widget.Button +import androidx.annotation.ColorInt +import com.nextcloud.android.common.ui.theme.utils.AndroidViewThemeUtils +import com.nextcloud.android.common.ui.theme.utils.ColorRole +import com.owncloud.android.R + +class IonosAndroidViewThemeUtils( + private val platformUtil: AndroidViewThemeUtils, +) { + fun themeSystemBars(activity: Activity) { + platformUtil.colorStatusBar(activity, activity.getSystemBarsColor()) + } + + fun themeSystemBars(activity: Activity, @ColorInt color: Int) { + platformUtil.colorStatusBar(activity, color) + } + + fun resetSystemBars(activity: Activity) { + platformUtil.colorStatusBar(activity, activity.getSystemBarsColor()) + } + + @JvmOverloads + fun colorViewBackground(view: View, colorRole: ColorRole = ColorRole.SURFACE) {} + + fun themeDialog(view: View) {} + + fun colorTextButtons(vararg buttons: Button) {} + + @ColorInt + private fun Context.getSystemBarsColor(): Int = getColor(R.color.system_bars_color) +} diff --git a/app/src/main/java/com/ionos/utils/IonosDialogViewThemeUtils.kt b/app/src/main/java/com/ionos/utils/IonosDialogViewThemeUtils.kt new file mode 100644 index 000000000000..54757836a435 --- /dev/null +++ b/app/src/main/java/com/ionos/utils/IonosDialogViewThemeUtils.kt @@ -0,0 +1,67 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.ionos.utils + +import android.content.Context +import android.content.res.ColorStateList +import android.graphics.drawable.Drawable +import android.os.Build +import android.widget.ImageView +import android.widget.TextView +import com.google.android.material.button.MaterialButton +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.google.android.material.shape.MaterialShapeDrawable +import com.ionos.annotation.IonosCustomization +import com.nextcloud.android.common.ui.theme.utils.DialogViewThemeUtils +import com.owncloud.android.R + +class IonosDialogViewThemeUtils( + private val delegate: DialogViewThemeUtils, +) { + + @IonosCustomization + fun colorDialogMenuText(button: MaterialButton) { + button.setTextColor(button.context.getColor(R.color.text_color)) + button.iconTint = ColorStateList.valueOf(button.context.getColor(R.color.default_icon_color)) + } + + @IonosCustomization + fun colorMaterialAlertDialogBackground(context: Context, dialogBuilder: MaterialAlertDialogBuilder) { + val materialShapeDrawable = MaterialShapeDrawable( + context, + null, + com.google.android.material.R.attr.alertDialogStyle, + com.google.android.material.R.style.MaterialAlertDialog_MaterialComponents, + ) + materialShapeDrawable.initializeElevationOverlay(context) + materialShapeDrawable.fillColor = ColorStateList.valueOf(context.getColor(R.color.bg_default)) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + val radius = context.resources.getDimension(R.dimen.dialogBorderRadius) + materialShapeDrawable.setCornerSize(radius) + } + + dialogBuilder.background = materialShapeDrawable + } + + fun colorDialogHeadline(textView: TextView) { + delegate.colorDialogHeadline(textView) + } + + fun colorDialogIcon(icon: ImageView) { + delegate.colorDialogIcon(icon) + } + + fun colorDialogSupportingText(textView: TextView) { + delegate.colorDialogSupportingText(textView) + } + + fun colorMaterialAlertDialogIcon(context: Context, drawableId: Int): Drawable { + return delegate.colorMaterialAlertDialogIcon(context, drawableId) + } +} diff --git a/app/src/main/java/com/ionos/utils/IonosMaterialViewThemeUtils.kt b/app/src/main/java/com/ionos/utils/IonosMaterialViewThemeUtils.kt new file mode 100644 index 000000000000..568acba78970 --- /dev/null +++ b/app/src/main/java/com/ionos/utils/IonosMaterialViewThemeUtils.kt @@ -0,0 +1,127 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.ionos.utils + +import android.util.TypedValue +import androidx.core.content.ContextCompat +import androidx.core.view.setPadding +import androidx.core.view.updatePadding +import com.google.android.material.button.MaterialButton +import com.google.android.material.chip.Chip +import com.google.android.material.floatingactionbutton.FloatingActionButton +import com.google.android.material.progressindicator.LinearProgressIndicator +import com.google.android.material.snackbar.Snackbar +import com.google.android.material.textfield.TextInputLayout +import com.nextcloud.android.common.ui.theme.utils.ColorRole +import com.nextcloud.android.common.ui.theme.utils.MaterialViewThemeUtils +import com.nextcloud.android.common.ui.util.buildColorStateList +import com.owncloud.android.R + +class IonosMaterialViewThemeUtils( + private val delegate: MaterialViewThemeUtils, +) { + + fun colorMaterialButtonPrimaryTonal(button: MaterialButton) { + colorMaterialButtonPrimaryFilled(button) + } + + fun colorMaterialButtonPrimaryBorderless(button: MaterialButton) { + colorMaterialButtonPrimaryOutlined(button) + } + + fun colorMaterialButtonPrimaryFilled(button: MaterialButton) { + button.backgroundTintList = ContextCompat.getColorStateList(button.context, R.color.filled_button_bg_color) + button.iconTint = ContextCompat.getColorStateList(button.context, R.color.filled_button_text_color) + button.setTextColor(ContextCompat.getColorStateList(button.context, R.color.filled_button_text_color)) + val textPadding = button.resources.getDimension(R.dimen.button_text_padding).toInt() + button.updatePadding(left = textPadding, right = textPadding) + } + + fun colorMaterialButtonPrimaryOutlined(button: MaterialButton) { + button.backgroundTintList = ContextCompat.getColorStateList(button.context, R.color.outlined_button_bg_color) + button.iconTint = ContextCompat.getColorStateList(button.context, R.color.outlined_button_text_color) + button.strokeColor = ContextCompat.getColorStateList(button.context, R.color.outlined_button_stroke_color) + button.strokeWidth = button.resources.getDimension(R.dimen.outlined_button_stroke_width).toInt() + button.setTextColor(ContextCompat.getColorStateList(button.context, R.color.outlined_button_text_color)) + val textPadding = button.resources.getDimension(R.dimen.button_text_padding).toInt() + button.updatePadding(left = textPadding, right = textPadding) + } + + fun colorMaterialButtonText(button: MaterialButton) { + button.setTextColor(ContextCompat.getColorStateList(button.context, R.color.outlined_button_text_color)) + } + + fun themeSnackbar(snackbar: Snackbar) {} + + fun colorTextInputLayout(textInputLayout: TextInputLayout) { + val context = textInputLayout.context + + val boxStrokeWidthRes = R.dimen.text_input_box_stroke_width + val cornerRadiusRes = R.dimen.text_input_box_corner_radius + val boxStrokeColorStateList = buildColorStateList( + -android.R.attr.state_focused to context.getColor(R.color.text_input_border_stroke_color), + android.R.attr.state_focused to context.getColor(R.color.text_input_focused_border_stroke_color), + ) + + textInputLayout.setBoxStrokeWidthResource(boxStrokeWidthRes) + textInputLayout.setBoxStrokeWidthFocusedResource(boxStrokeWidthRes) + textInputLayout.setBoxCornerRadiiResources(cornerRadiusRes, cornerRadiusRes, cornerRadiusRes, cornerRadiusRes) + textInputLayout.setBoxStrokeColorStateList(boxStrokeColorStateList) + + val errorColorStateList = buildColorStateList( + -android.R.attr.state_focused to context.getColor(R.color.text_input_error_color), + android.R.attr.state_focused to context.getColor(R.color.text_input_error_color), + ) + + textInputLayout.setErrorIconTintList(errorColorStateList) + textInputLayout.setErrorTextColor(errorColorStateList) + textInputLayout.boxStrokeErrorColor = errorColorStateList + + val hintTextColorStateList = buildColorStateList( + -android.R.attr.state_focused to context.getColor(R.color.text_input_hint_text_color), + android.R.attr.state_focused to context.getColor(R.color.text_input_focused_hint_text_color), + ) + + textInputLayout.defaultHintTextColor = hintTextColorStateList + + val padding = context.resources.getDimension(R.dimen.text_input_padding).toInt() + val textSize = context.resources.getDimension(R.dimen.text_input_text_size) + + textInputLayout.editText?.setPadding(padding) + textInputLayout.editText?.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize) + textInputLayout.editText?.highlightColor = context.getColor(R.color.text_input_highlight_color) + } + + fun themeFAB(fab: FloatingActionButton) { + delegate.themeFAB(fab) + } + + fun colorMaterialButtonFilledOnPrimary(btn: MaterialButton) { + delegate.colorMaterialButtonFilledOnPrimary(btn) + } + + fun colorMaterialButtonOutlinedOnPrimary(btn: MaterialButton) { + delegate.colorMaterialButtonOutlinedOnPrimary(btn) + } + + fun colorMaterialTextButton(btn: MaterialButton) { + delegate.colorMaterialTextButton(btn) + } + + fun colorProgressBar(progressBar: LinearProgressIndicator) { + delegate.colorProgressBar(progressBar) + } + + fun themeChipSuggestion(chip: Chip) { + delegate.themeChipSuggestion(chip) + } + + fun colorTextInputLayout(til: TextInputLayout, colorRole: ColorRole) { + delegate.colorTextInputLayout(til, colorRole) + } +} diff --git a/app/src/main/java/com/ionos/utils/IonosViewThemeUtils.kt b/app/src/main/java/com/ionos/utils/IonosViewThemeUtils.kt new file mode 100644 index 000000000000..adc3736565e2 --- /dev/null +++ b/app/src/main/java/com/ionos/utils/IonosViewThemeUtils.kt @@ -0,0 +1,20 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO AG. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.ionos.utils + +import com.ionos.annotation.IonosCustomization +import com.nextcloud.android.common.ui.theme.utils.AndroidViewThemeUtils + +@IonosCustomization +class IonosViewThemeUtils( + private val platformUtil: AndroidViewThemeUtils, +) { + + @JvmField + val platform = IonosAndroidViewThemeUtils(platformUtil) +} \ No newline at end of file diff --git a/app/src/main/java/com/ionos/utils/context/ContextUtils.kt b/app/src/main/java/com/ionos/utils/context/ContextUtils.kt new file mode 100644 index 000000000000..4b68b515e863 --- /dev/null +++ b/app/src/main/java/com/ionos/utils/context/ContextUtils.kt @@ -0,0 +1,18 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +@file:JvmName("ContextUtils") + +package com.ionos.utils.context + +import android.content.Context +import android.content.res.Configuration + +fun Context.isDarkMode(): Boolean { + val uiMode = resources.configuration.uiMode + return (uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES +} diff --git a/app/src/main/java/com/ionos/utils/text/CharSequenceUtils.kt b/app/src/main/java/com/ionos/utils/text/CharSequenceUtils.kt new file mode 100644 index 000000000000..c83acfa7965a --- /dev/null +++ b/app/src/main/java/com/ionos/utils/text/CharSequenceUtils.kt @@ -0,0 +1,46 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +@file:JvmName("CharSequenceUtils") + +package com.ionos.utils.text + +import android.text.Annotation +import android.text.SpannableString +import android.text.Spanned +import android.text.TextPaint +import android.text.style.ClickableSpan +import android.view.View + +fun CharSequence.convertAnnotatedTextToLinks( + linkColor: Int, + linkUnderline: Boolean, + linkHandler: (type: String) -> Unit, +): SpannableString { + val spannableString = SpannableString(this) + val annotations = spannableString.getSpans(0, spannableString.length, Annotation::class.java) + + annotations.forEach { annotation -> + val start = spannableString.getSpanStart(annotation) + val end = spannableString.getSpanEnd(annotation) + + val clickableSpan = object : ClickableSpan() { + override fun updateDrawState(paint: TextPaint) { + paint.color = linkColor + paint.isUnderlineText = linkUnderline + } + + override fun onClick(widget: View) { + linkHandler.invoke(annotation.value) + } + } + + spannableString.setSpan(clickableSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) + } + + return spannableString +} diff --git a/app/src/main/java/com/nextcloud/client/account/UserAccountManagerImpl.java b/app/src/main/java/com/nextcloud/client/account/UserAccountManagerImpl.java index 1554e68e8113..121a03f06b39 100644 --- a/app/src/main/java/com/nextcloud/client/account/UserAccountManagerImpl.java +++ b/app/src/main/java/com/nextcloud/client/account/UserAccountManagerImpl.java @@ -19,6 +19,8 @@ import android.preference.PreferenceManager; import android.text.TextUtils; +import com.ionos.annotation.IonosCustomization; +import com.ionos.authorization_method.AuthorizationMethodActivity; import com.nextcloud.common.NextcloudClient; import com.nextcloud.utils.extensions.AccountExtensionsKt; import com.owncloud.android.MainApp; @@ -396,9 +398,9 @@ private String getAccountType() { } @Override + @IonosCustomization public void startAccountCreation(final Activity activity) { - Intent intent = new Intent(context, AuthenticatorActivity.class); - + Intent intent = AuthorizationMethodActivity.createInstance(context); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } diff --git a/app/src/main/java/com/nextcloud/client/di/AppComponent.java b/app/src/main/java/com/nextcloud/client/di/AppComponent.java index da9d7cb33d94..4c6933aaeafb 100644 --- a/app/src/main/java/com/nextcloud/client/di/AppComponent.java +++ b/app/src/main/java/com/nextcloud/client/di/AppComponent.java @@ -9,6 +9,7 @@ import android.app.Application; +import com.ionos.di.StratoModule; import com.nextcloud.appReview.InAppReviewModule; import com.nextcloud.client.appinfo.AppInfoModule; import com.nextcloud.client.database.DatabaseModule; @@ -46,7 +47,8 @@ ThemeModule.class, DatabaseModule.class, DispatcherModule.class, - VariantModule.class + VariantModule.class, + StratoModule.class, }) @Singleton public interface AppComponent { diff --git a/app/src/main/java/com/nextcloud/client/editimage/EditImageActivity.kt b/app/src/main/java/com/nextcloud/client/editimage/EditImageActivity.kt index 386c1bbe8e05..6c2241c4e474 100644 --- a/app/src/main/java/com/nextcloud/client/editimage/EditImageActivity.kt +++ b/app/src/main/java/com/nextcloud/client/editimage/EditImageActivity.kt @@ -20,6 +20,7 @@ import androidx.core.graphics.drawable.DrawableCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat import com.canhub.cropper.CropImageView +import com.ionos.annotation.IonosCustomization import com.nextcloud.client.di.Injectable import com.nextcloud.client.jobs.upload.FileUploadHelper import com.nextcloud.client.jobs.upload.FileUploadWorker @@ -33,7 +34,9 @@ import com.owncloud.android.operations.UploadFileOperation import com.owncloud.android.ui.activity.FileActivity import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.MimeType +import com.owncloud.android.utils.theme.ViewThemeUtils import java.io.File +import javax.inject.Inject class EditImageActivity : FileActivity(), @@ -46,6 +49,9 @@ class EditImageActivity : private lateinit var file: OCFile private lateinit var format: Bitmap.CompressFormat + @Inject + lateinit var viewThemeUtils: ViewThemeUtils + companion object { const val EXTRA_FILE = "FILE" const val OPEN_IMAGE_EDITOR = "OPEN_IMAGE_EDITOR" @@ -64,6 +70,7 @@ class EditImageActivity : } } + @IonosCustomization override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -82,8 +89,7 @@ class EditImageActivity : val windowInsetsController = WindowCompat.getInsetsController(window, window.decorView) windowInsetsController.hide(WindowInsetsCompat.Type.statusBars()) - window.statusBarColor = ContextCompat.getColor(this, R.color.black) - window.navigationBarColor = getColor(R.color.black) + viewThemeUtils.ionos.platform.themeSystemBars(this,getColor(R.color.edit_image_background)) setupCropper() } diff --git a/app/src/main/java/com/nextcloud/client/jobs/AccountRemovalWork.kt b/app/src/main/java/com/nextcloud/client/jobs/AccountRemovalWork.kt index ebcb5f7dd4d3..408a13575bfc 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/AccountRemovalWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/AccountRemovalWork.kt @@ -17,6 +17,7 @@ import android.text.TextUtils import androidx.work.Worker import androidx.work.WorkerParameters import com.google.gson.Gson +import com.ionos.privacy.PrivacyPreferences import com.nextcloud.client.account.User import com.nextcloud.client.account.UserAccountManager import com.nextcloud.client.core.Clock @@ -58,7 +59,8 @@ class AccountRemovalWork( private val clock: Clock, private val eventBus: EventBus, private val preferences: AppPreferences, - private val syncedFolderProvider: SyncedFolderProvider + private val syncedFolderProvider: SyncedFolderProvider, + private val privacyPreferences: PrivacyPreferences, ) : Worker(context, params) { companion object { @@ -112,6 +114,8 @@ class AccountRemovalWork( preferences.currentAccountName = "" } + privacyPreferences.removeDataProtectionProcessed(accountName) + // remove all files storageManager.removeLocalFiles(user, storageManager) diff --git a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobFactory.kt b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobFactory.kt index b68553601d80..f2e7144e0031 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobFactory.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobFactory.kt @@ -15,6 +15,7 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.work.ListenableWorker import androidx.work.WorkerFactory import androidx.work.WorkerParameters +import com.ionos.privacy.PrivacyPreferences import com.nextcloud.client.account.UserAccountManager import com.nextcloud.client.core.Clock import com.nextcloud.client.device.DeviceInfo @@ -31,6 +32,8 @@ import com.owncloud.android.datamodel.ArbitraryDataProvider import com.owncloud.android.datamodel.SyncedFolderProvider import com.owncloud.android.datamodel.UploadsStorageManager import com.owncloud.android.utils.theme.ViewThemeUtils +import com.ionos.scanbot.license.ScanbotLicenseDownloadWorker +import com.ionos.scanbot.license.ScanbotLicenseJobFactory import org.greenrobot.eventbus.EventBus import javax.inject.Inject import javax.inject.Provider @@ -60,7 +63,9 @@ class BackgroundJobFactory @Inject constructor( private val viewThemeUtils: Provider, private val localBroadcastManager: Provider, private val generatePdfUseCase: GeneratePDFUseCase, - private val syncedFolderProvider: SyncedFolderProvider + private val syncedFolderProvider: SyncedFolderProvider, + private val scanbotLicenseJobFactory: ScanbotLicenseJobFactory, + private val privacyPreferences: PrivacyPreferences, ) : WorkerFactory() { @SuppressLint("NewApi") @@ -92,6 +97,7 @@ class BackgroundJobFactory @Inject constructor( FilesExportWork::class -> createFilesExportWork(context, workerParameters) FileUploadWorker::class -> createFilesUploadWorker(context, workerParameters) FileDownloadWorker::class -> createFilesDownloadWorker(context, workerParameters) + ScanbotLicenseDownloadWorker::class -> scanbotLicenseJobFactory.create(context, workerParameters) GeneratePdfFromImagesWork::class -> createPDFGenerateWork(context, workerParameters) HealthStatusWork::class -> createHealthStatusWork(context, workerParameters) TestJob::class -> createTestJob(context, workerParameters) @@ -220,7 +226,8 @@ class BackgroundJobFactory @Inject constructor( clock, eventBus, preferences, - syncedFolderProvider + syncedFolderProvider, + privacyPreferences, ) } diff --git a/app/src/main/java/com/nextcloud/client/logger/ui/LogsActivity.kt b/app/src/main/java/com/nextcloud/client/logger/ui/LogsActivity.kt index 642d22ba0fcc..9f0688af128f 100644 --- a/app/src/main/java/com/nextcloud/client/logger/ui/LogsActivity.kt +++ b/app/src/main/java/com/nextcloud/client/logger/ui/LogsActivity.kt @@ -16,6 +16,7 @@ import androidx.lifecycle.Observer import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView +import com.ionos.annotation.IonosCustomization import com.nextcloud.client.di.ViewModelFactory import com.owncloud.android.R import com.owncloud.android.databinding.LogsActivityBinding @@ -75,12 +76,12 @@ class LogsActivity : ToolbarActivity() { } } + @IonosCustomization override fun onCreateOptionsMenu(menu: Menu): Boolean { menuInflater.inflate(R.menu.activity_logs, menu) (menu.findItem(R.id.action_search).actionView as SearchView).apply { setOnQueryTextListener(searchBoxListener) - viewThemeUtils.androidx.themeToolbarSearchView(this) } return super.onCreateOptionsMenu(menu) } diff --git a/app/src/main/java/com/nextcloud/client/media/Player.kt b/app/src/main/java/com/nextcloud/client/media/Player.kt index 8990fcdcca04..befcd7280a23 100644 --- a/app/src/main/java/com/nextcloud/client/media/Player.kt +++ b/app/src/main/java/com/nextcloud/client/media/Player.kt @@ -261,9 +261,7 @@ internal class Player( } override fun seekTo(pos: Int) { - if (stateMachine.isInState(State.PLAYING)) { - mediaPlayer?.seekTo(pos) - } + mediaPlayer?.seekTo(pos) } override fun getCurrentPosition(): Int { diff --git a/app/src/main/java/com/nextcloud/client/media/PlayerService.kt b/app/src/main/java/com/nextcloud/client/media/PlayerService.kt index ec6012f533ff..e518a83a3bd6 100644 --- a/app/src/main/java/com/nextcloud/client/media/PlayerService.kt +++ b/app/src/main/java/com/nextcloud/client/media/PlayerService.kt @@ -7,8 +7,11 @@ package com.nextcloud.client.media import android.app.PendingIntent +import android.app.PendingIntent.FLAG_IMMUTABLE import android.app.Service import android.content.Intent +import android.content.Intent.FLAG_ACTIVITY_REORDER_TO_FRONT +import android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP import android.media.AudioManager import android.os.Build import android.os.Bundle @@ -17,6 +20,7 @@ import android.widget.MediaController import android.widget.Toast import androidx.core.app.NotificationCompat import androidx.localbroadcastmanager.content.LocalBroadcastManager +import com.ionos.annotation.IonosCustomization import com.nextcloud.client.account.User import com.nextcloud.client.network.ClientFactory import com.nextcloud.utils.ForegroundServiceHelper @@ -174,9 +178,14 @@ class PlayerService : Service() { stopServiceAndRemoveNotification(file) } + @IonosCustomization("clickable notification") private fun startForeground(currentFile: OCFile) { val ticker = String.format(getString(R.string.media_notif_ticker), getString(R.string.app_name)) val content = getString(R.string.media_state_playing, currentFile.getFileName()) + val intent = Intent(this, PreviewMediaActivity::class.java) + .apply { + flags = FLAG_ACTIVITY_REORDER_TO_FRONT or FLAG_ACTIVITY_SINGLE_TOP + } notificationBuilder.run { setSmallIcon(R.drawable.ic_play_arrow) @@ -184,6 +193,9 @@ class PlayerService : Service() { setOngoing(true) setContentTitle(ticker) setContentText(content) + setContentIntent( + PendingIntent.getActivity(mContext, 0, intent, FLAG_IMMUTABLE) + ) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_MEDIA) diff --git a/app/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java b/app/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java index d7a06ab48e4c..d14e74e5b1fd 100644 --- a/app/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java +++ b/app/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java @@ -17,11 +17,13 @@ import com.google.common.reflect.TypeToken; import com.google.gson.Gson; +import com.ionos.annotation.IonosCustomization; import com.nextcloud.appReview.AppReviewShownModel; import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.account.UserAccountManagerImpl; import com.nextcloud.client.jobs.LogEntry; +import com.owncloud.android.R; import com.owncloud.android.datamodel.ArbitraryDataProvider; import com.owncloud.android.datamodel.ArbitraryDataProviderImpl; import com.owncloud.android.datamodel.FileDataStorageManager; @@ -477,14 +479,9 @@ public void setUploaderBehaviour(int uploaderBehaviour) { * @return grid columns grid columns */ @Override + @IonosCustomization public float getGridColumns() { - float columns = preferences.getFloat(AUTO_PREF__GRID_COLUMNS, DEFAULT_GRID_COLUMN); - - if (columns < 0) { - return DEFAULT_GRID_COLUMN; - } else { - return columns; - } + return context.getResources().getInteger(R.integer.grid_mode_column_count); } /** @@ -493,8 +490,9 @@ public float getGridColumns() { * @param gridColumns the uploader behavior */ @Override + @IonosCustomization public void setGridColumns(float gridColumns) { - preferences.edit().putFloat(AUTO_PREF__GRID_COLUMNS, gridColumns).apply(); + // Do nothing } @Override diff --git a/app/src/main/java/com/nextcloud/ui/ChooseAccountDialogFragment.kt b/app/src/main/java/com/nextcloud/ui/ChooseAccountDialogFragment.kt index 27e3fc42768f..e2e09d246ee8 100644 --- a/app/src/main/java/com/nextcloud/ui/ChooseAccountDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/ui/ChooseAccountDialogFragment.kt @@ -19,6 +19,7 @@ import android.view.ViewGroup import android.widget.ImageView import androidx.fragment.app.DialogFragment import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.ionos.annotation.IonosCustomization import com.nextcloud.client.account.User import com.nextcloud.client.account.UserAccountManager import com.nextcloud.client.di.Injectable @@ -157,9 +158,10 @@ class ChooseAccountDialogFragment : themeViews() } + @IonosCustomization private fun themeViews() { viewThemeUtils.platform.themeDialogDivider(binding.separatorLine) - viewThemeUtils.platform.themeDialog(binding.root) + viewThemeUtils.ionos.platform.themeDialog(binding.root) viewThemeUtils.material.colorMaterialTextButton(binding.setStatus) viewThemeUtils.dialog.colorDialogMenuText(binding.setStatus) diff --git a/app/src/main/java/com/nextcloud/ui/SetStatusDialogFragment.kt b/app/src/main/java/com/nextcloud/ui/SetStatusDialogFragment.kt index 9ea25e5caca9..9d8baec8eb11 100644 --- a/app/src/main/java/com/nextcloud/ui/SetStatusDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/ui/SetStatusDialogFragment.kt @@ -30,6 +30,7 @@ import com.google.android.material.card.MaterialCardView import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.gson.Gson import com.google.gson.reflect.TypeToken +import com.ionos.annotation.IonosCustomization import com.nextcloud.client.account.User import com.nextcloud.client.account.UserAccountManager import com.nextcloud.client.core.AsyncRunner @@ -134,6 +135,7 @@ class SetStatusDialogFragment : return builder.create() } + @IonosCustomization() @SuppressLint("DefaultLocale") override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) @@ -200,7 +202,7 @@ class SetStatusDialogFragment : viewThemeUtils.material.colorMaterialButtonPrimaryTonal(binding.setStatus) viewThemeUtils.material.colorTextInputLayout(binding.customStatusInputContainer) - viewThemeUtils.platform.themeDialog(binding.root) + viewThemeUtils.ionos.platform.themeDialog(binding.root) } private fun updateCurrentStatusViews(it: Status) { diff --git a/app/src/main/java/com/nextcloud/ui/fileactions/FileAction.kt b/app/src/main/java/com/nextcloud/ui/fileactions/FileAction.kt index 4807c6d2b87c..3ba3e375f737 100644 --- a/app/src/main/java/com/nextcloud/ui/fileactions/FileAction.kt +++ b/app/src/main/java/com/nextcloud/ui/fileactions/FileAction.kt @@ -10,49 +10,51 @@ package com.nextcloud.ui.fileactions import androidx.annotation.DrawableRes import androidx.annotation.IdRes import androidx.annotation.StringRes +import com.ionos.annotation.IonosCustomization import com.owncloud.android.R +@IonosCustomization("Custom icons") enum class FileAction(@IdRes val id: Int, @StringRes val title: Int, @DrawableRes val icon: Int? = null) { // selection - SELECT_ALL(R.id.action_select_all_action_menu, R.string.select_all, R.drawable.ic_select_all), - SELECT_NONE(R.id.action_deselect_all_action_menu, R.string.deselect_all, R.drawable.ic_select_none), + SELECT_ALL(R.id.action_select_all_action_menu, R.string.select_all, R.drawable.ic_file_action_select_all), + SELECT_NONE(R.id.action_deselect_all_action_menu, R.string.deselect_all, R.drawable.ic_file_action_select_none), // generic file actions - EDIT(R.id.action_edit, R.string.action_edit, R.drawable.ic_edit), - SEE_DETAILS(R.id.action_see_details, R.string.actionbar_see_details, R.drawable.ic_information_outline), - REMOVE_FILE(R.id.action_remove_file, R.string.common_remove, R.drawable.ic_delete), + EDIT(R.id.action_edit, R.string.action_edit, R.drawable.ic_file_action_edit), + SEE_DETAILS(R.id.action_see_details, R.string.actionbar_see_details, R.drawable.ic_file_action_see_details), + REMOVE_FILE(R.id.action_remove_file, R.string.common_remove, R.drawable.ic_file_action_remove_file), // File moving - RENAME_FILE(R.id.action_rename_file, R.string.common_rename, R.drawable.ic_rename), - MOVE_OR_COPY(R.id.action_move_or_copy, R.string.actionbar_move_or_copy, R.drawable.ic_external), + RENAME_FILE(R.id.action_rename_file, R.string.common_rename, R.drawable.ic_file_action_rename_file), + MOVE_OR_COPY(R.id.action_move_or_copy, R.string.actionbar_move_or_copy, R.drawable.ic_file_action_move_or_copy), // favorites - FAVORITE(R.id.action_favorite, R.string.favorite, R.drawable.ic_star), - UNSET_FAVORITE(R.id.action_unset_favorite, R.string.unset_favorite, R.drawable.ic_star_outline), + FAVORITE(R.id.action_favorite, R.string.favorite, R.drawable.ic_file_action_favorite), + UNSET_FAVORITE(R.id.action_unset_favorite, R.string.unset_favorite, R.drawable.ic_file_action_unset_favorite), // Uploads and downloads - DOWNLOAD_FILE(R.id.action_download_file, R.string.filedetails_download, R.drawable.ic_cloud_download), - SYNC_FILE(R.id.action_sync_file, R.string.filedetails_sync_file, R.drawable.ic_cloud_sync_on), - CANCEL_SYNC(R.id.action_cancel_sync, R.string.common_cancel_sync, R.drawable.ic_cloud_sync_off), + DOWNLOAD_FILE(R.id.action_download_file, R.string.filedetails_download, R.drawable.ic_file_action_download_file), + SYNC_FILE(R.id.action_sync_file, R.string.filedetails_sync_file, R.drawable.ic_file_action_sync_file), + CANCEL_SYNC(R.id.action_cancel_sync, R.string.common_cancel_sync, R.drawable.ic_file_action_cancel_sync), // File sharing - EXPORT_FILE(R.id.action_export_file, R.string.filedetails_export, R.drawable.ic_export), - SEND_SHARE_FILE(R.id.action_send_share_file, R.string.action_send_share, R.drawable.ic_share), - SEND_FILE(R.id.action_send_file, R.string.common_send, R.drawable.ic_share), - OPEN_FILE_WITH(R.id.action_open_file_with, R.string.actionbar_open_with, R.drawable.ic_external), - STREAM_MEDIA(R.id.action_stream_media, R.string.stream, R.drawable.ic_play_arrow), - SET_AS_WALLPAPER(R.id.action_set_as_wallpaper, R.string.set_picture_as, R.drawable.ic_wallpaper), + EXPORT_FILE(R.id.action_export_file, R.string.filedetails_export, R.drawable.ic_file_action_export_file), + SEND_SHARE_FILE(R.id.action_send_share_file, R.string.action_send_share, R.drawable.ic_file_action_share_file), + SEND_FILE(R.id.action_send_file, R.string.common_send, R.drawable.ic_file_action_share_file), + OPEN_FILE_WITH(R.id.action_open_file_with, R.string.actionbar_open_with, R.drawable.ic_file_action_open_file_with), + STREAM_MEDIA(R.id.action_stream_media, R.string.stream, R.drawable.ic_file_action_stream_media), + SET_AS_WALLPAPER(R.id.action_set_as_wallpaper, R.string.set_picture_as, R.drawable.ic_file_action_set_as_wallpaper), // Encryption - SET_ENCRYPTED(R.id.action_encrypted, R.string.encrypted, R.drawable.ic_encrypt), - UNSET_ENCRYPTED(R.id.action_unset_encrypted, R.string.unset_encrypted, R.drawable.ic_decrypt), + SET_ENCRYPTED(R.id.action_encrypted, R.string.encrypted, R.drawable.ic_file_action_set_encrypted), + UNSET_ENCRYPTED(R.id.action_unset_encrypted, R.string.unset_encrypted, R.drawable.ic_file_action_unset_encrypted), // locks - UNLOCK_FILE(R.id.action_unlock_file, R.string.unlock_file, R.drawable.ic_lock_open_white), - LOCK_FILE(R.id.action_lock_file, R.string.lock_file, R.drawable.ic_lock), + UNLOCK_FILE(R.id.action_unlock_file, R.string.unlock_file, R.drawable.ic_file_action_unlock_file), + LOCK_FILE(R.id.action_lock_file, R.string.lock_file, R.drawable.ic_file_action_lock_file), // Shortcuts - PIN_TO_HOMESCREEN(R.id.action_pin_to_homescreen, R.string.pin_home, R.drawable.add_to_home_screen); + PIN_TO_HOMESCREEN(R.id.action_pin_to_homescreen, R.string.pin_home, R.drawable.ic_file_action_pin_to_homescreen); companion object { /** diff --git a/app/src/main/java/com/nextcloud/ui/fileactions/FileActionsBottomSheet.kt b/app/src/main/java/com/nextcloud/ui/fileactions/FileActionsBottomSheet.kt index 1269459f2a94..c70db50f06a8 100644 --- a/app/src/main/java/com/nextcloud/ui/fileactions/FileActionsBottomSheet.kt +++ b/app/src/main/java/com/nextcloud/ui/fileactions/FileActionsBottomSheet.kt @@ -18,7 +18,6 @@ import android.view.View import android.view.ViewGroup import android.widget.Toast import androidx.annotation.IdRes -import androidx.appcompat.content.res.AppCompatResources import androidx.core.os.bundleOf import androidx.core.view.isEmpty import androidx.core.view.isVisible @@ -29,6 +28,7 @@ import androidx.lifecycle.ViewModelProvider import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import com.ionos.annotation.IonosCustomization import com.nextcloud.android.common.ui.theme.utils.ColorRole import com.nextcloud.client.account.CurrentAccountProvider import com.nextcloud.client.di.Injectable @@ -94,7 +94,7 @@ class FileActionsBottomSheet : BottomSheetDialogFragment(), Injectable { bottomSheetDialog.behavior.state = BottomSheetBehavior.STATE_EXPANDED bottomSheetDialog.behavior.skipCollapsed = true - viewThemeUtils.platform.colorViewBackground(binding.bottomSheet, ColorRole.SURFACE) + viewThemeUtils.ionos.platform.colorViewBackground(binding.bottomSheet, ColorRole.SURFACE) return binding.root } @@ -145,11 +145,9 @@ class FileActionsBottomSheet : BottomSheetDialogFragment(), Injectable { } } + @IonosCustomization("Set thumbnail drawable without tint") private fun setMultipleFilesThumbnail() { - context?.let { - val drawable = viewThemeUtils.platform.tintDrawable(it, R.drawable.file_multiple, ColorRole.PRIMARY) - binding.thumbnailLayout.thumbnail.setImageDrawable(drawable) - } + binding.thumbnailLayout.thumbnail.setImageResource(R.drawable.file_multiple) } override fun onDestroyView() { @@ -270,6 +268,7 @@ class FileActionsBottomSheet : BottomSheetDialogFragment(), Injectable { binding.title.text = resources.getQuantityString(R.plurals.file_list__footer__file, fileCount, fileCount) } + @IonosCustomization("Set icon drawable without tint") private fun inflateActionView(action: FileAction): View { val itemBinding = FileActionsBottomSheetItemBinding.inflate(layoutInflater, binding.fileActionsList, false) .apply { @@ -278,12 +277,7 @@ class FileActionsBottomSheet : BottomSheetDialogFragment(), Injectable { } text.setText(action.title) if (action.icon != null) { - val drawable = - viewThemeUtils.platform.tintDrawable( - requireContext(), - AppCompatResources.getDrawable(requireContext(), action.icon)!! - ) - icon.setImageDrawable(drawable) + icon.setImageResource(action.icon) } } return itemBinding.root diff --git a/app/src/main/java/com/nmc/android/ui/LauncherActivity.kt b/app/src/main/java/com/nmc/android/ui/LauncherActivity.kt index b4c1272a76ee..fbd877f0012f 100644 --- a/app/src/main/java/com/nmc/android/ui/LauncherActivity.kt +++ b/app/src/main/java/com/nmc/android/ui/LauncherActivity.kt @@ -16,6 +16,10 @@ import android.text.TextUtils import android.view.View import androidx.annotation.VisibleForTesting import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen +import com.ionos.annotation.IonosCustomization +import com.ionos.privacy.DataProtectionActivity +import com.ionos.privacy.PrivacyPreferences +import com.nextcloud.client.account.UserAccountManager import com.nextcloud.client.preferences.AppPreferences import com.owncloud.android.R import com.owncloud.android.databinding.ActivitySplashBinding @@ -30,6 +34,9 @@ class LauncherActivity : BaseActivity() { @Inject lateinit var appPreferences: AppPreferences + @Inject + lateinit var privacyPreferences: PrivacyPreferences + override fun onCreate(savedInstanceState: Bundle?) { // Mandatory to call this before super method to show system launch screen for api level 31+ installSplashScreen() @@ -61,10 +68,16 @@ class LauncherActivity : BaseActivity() { } } + @IonosCustomization private fun scheduleSplashScreen() { Handler(Looper.getMainLooper()).postDelayed({ if (user.isPresent) { - startActivity(Intent(this, FileDisplayActivity::class.java)) + val intent = Intent(this, FileDisplayActivity::class.java) + if (privacyPreferences.isDataProtectionProcessed(userAccountManager.currentOwnCloudAccount?.name)) { + startActivity(intent) + } else { + startActivity(DataProtectionActivity.createIntent(this, intent)) + } } finish() }, SPLASH_DURATION) diff --git a/app/src/main/java/com/owncloud/android/MainApp.java b/app/src/main/java/com/owncloud/android/MainApp.java index 8751bf429c2e..3806ed8fb06c 100644 --- a/app/src/main/java/com/owncloud/android/MainApp.java +++ b/app/src/main/java/com/owncloud/android/MainApp.java @@ -204,6 +204,7 @@ public class MainApp extends Application implements HasAndroidInjector { private static AppComponent appComponent; + /** * Temporary hack */ diff --git a/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java b/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java index c4b1cc81d559..6121ae66d8a3 100644 --- a/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java +++ b/app/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java @@ -46,7 +46,6 @@ import android.webkit.WebView; import android.widget.AdapterView; import android.widget.ArrayAdapter; -import android.widget.LinearLayout; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; import android.widget.Toast; @@ -57,7 +56,10 @@ import com.google.gson.Gson; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import com.ionos.annotation.IonosCustomization; import com.google.gson.reflect.TypeToken; +import com.ionos.privacy.DataProtectionActivity; +import com.ionos.privacy.PrivacyPreferences; import com.nextcloud.android.common.ui.color.ColorUtil; import com.nextcloud.android.common.ui.theme.utils.ColorRole; import com.nextcloud.client.account.User; @@ -66,7 +68,6 @@ import com.nextcloud.client.di.Injectable; import com.nextcloud.client.network.ClientFactory; import com.nextcloud.client.onboarding.FirstRunActivity; -import com.nextcloud.client.onboarding.OnboardingService; import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.common.PlainClient; import com.nextcloud.operations.PostMethod; @@ -249,7 +250,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity @Inject UserAccountManager accountManager; @Inject AppPreferences preferences; - @Inject OnboardingService onboarding; + @Inject PrivacyPreferences privacyPreferences; @Inject DeviceInfo deviceInfo; @Inject PassCodeManager passCodeManager; @Inject ViewThemeUtils.Factory viewThemeUtilsFactory; @@ -276,18 +277,15 @@ public AccountSetupBinding getAccountSetupBinding() { * IMPORTANT ENTRY POINT 1: activity is shown to the user */ @Override + @IonosCustomization protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); viewThemeUtils = viewThemeUtilsFactory.withPrimaryAsBackground(); - viewThemeUtils.platform.themeStatusBar(this, ColorRole.PRIMARY); // WebViewUtil webViewUtil = new WebViewUtil(this); Uri data = getIntent().getData(); boolean directLogin = data != null && data.toString().startsWith(getString(R.string.login_data_own_scheme)); - if (savedInstanceState == null && !directLogin) { - onboarding.launchFirstRunIfNeeded(this); - } onlyAdd = getIntent().getBooleanExtra(KEY_ONLY_ADD, false) || checkIfViaSSO(getIntent()); @@ -323,6 +321,10 @@ protected void onCreate(Bundle savedInstanceState) { mIsFirstAuthAttempt = savedInstanceState.getBoolean(KEY_AUTH_IS_FIRST_ATTEMPT_TAG); } + if (directLogin) { + return; + } + boolean webViewLoginMethod = false; String webloginUrl = null; @@ -337,7 +339,7 @@ protected void onCreate(Bundle savedInstanceState) { if (!TextUtils.isEmpty(webloginUrl)) { webViewLoginMethod = true; - } else if (getIntent().getBooleanExtra(EXTRA_USE_PROVIDER_AS_WEBLOGIN, false)) { + } else if (getIntent().getBooleanExtra(EXTRA_USE_PROVIDER_AS_WEBLOGIN, true)) { webViewLoginMethod = true; webloginUrl = getString(R.string.provider_registration_server); } else if (!TextUtils.isEmpty(getResources().getString(R.string.webview_login_url))) { @@ -352,6 +354,7 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(accountSetupWebviewBinding.getRoot()); anonymouslyPostLoginRequest(webloginUrl); // initWebViewLogin(webloginUrl, false); + initCancelButton(); } else { accountSetupBinding = AccountSetupBinding.inflate(getLayoutInflater()); setContentView(accountSetupBinding.getRoot()); @@ -367,12 +370,23 @@ protected void onCreate(Bundle savedInstanceState) { } else { showEnforcedServers(); } - - initServerPreFragment(savedInstanceState); - ProcessLifecycleOwner.get().getLifecycle().addObserver(lifecycleEventObserver); - - // webViewUtil.checkWebViewVersion(); } + + initServerPreFragment(savedInstanceState); + ProcessLifecycleOwner.get().getLifecycle().addObserver(lifecycleEventObserver); + + // webViewUtil.checkWebViewVersion(); + } + + @IonosCustomization + private void initCancelButton() { + MaterialButton cancelButton = accountSetupWebviewBinding.loginFlowV2.cancelButton; + + cancelButton.setOnClickListener(v -> { + loginFlowExecutorService.shutdown(); + ProcessLifecycleOwner.get().getLifecycle().removeObserver(lifecycleEventObserver); + finish(); + }); } private void showEnforcedServers() { @@ -822,6 +836,7 @@ public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { * AndroidManifest.xml file. */ @Override + @IonosCustomization protected void onNewIntent(Intent intent) { super.onNewIntent(intent); Log_OC.d(TAG, "onNewIntent()"); @@ -848,7 +863,7 @@ protected void onNewIntent(Intent intent) { } } - if (intent.getBooleanExtra(EXTRA_USE_PROVIDER_AS_WEBLOGIN, false)) { + if (intent.getBooleanExtra(EXTRA_USE_PROVIDER_AS_WEBLOGIN, true)) { accountSetupWebviewBinding = AccountSetupWebviewBinding.inflate(getLayoutInflater()); setContentView(accountSetupWebviewBinding.getRoot()); anonymouslyPostLoginRequest(getString(R.string.provider_registration_server)); @@ -972,13 +987,11 @@ private void checkOcServer() { * Tests the credentials entered by the user performing a check of existence on the root folder of the ownCloud * server. */ + @IonosCustomization private void checkBasicAuthorization(@Nullable String webViewUsername, @Nullable String webViewPassword) { - // be gentle with the user - IndeterminateProgressDialog dialog = IndeterminateProgressDialog.newInstance(R.string.auth_trying_to_login, - true); - FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); - ft.add(dialog, WAIT_DIALOG_TAG); - ft.commitAllowingStateLoss(); + if (accountSetupWebviewBinding != null) { + accountSetupWebviewBinding.loginFlowV2.tvAuthorizationDescription.setText(R.string.auth_trying_to_login); + } // validate credentials accessing the root folder OwnCloudCredentials credentials = OwnCloudCredentialsFactory.newBasicCredentials(webViewUsername, @@ -1112,12 +1125,11 @@ private void onGetServerInfoFinish(RemoteOperationResult result) { } // region LoginInfoView + @IonosCustomization private void initLoginInfoView() { - LinearLayout loginFlowLayout = accountSetupWebviewBinding.loginFlowV2.getRoot(); - MaterialButton cancelButton = accountSetupWebviewBinding.loginFlowV2.cancelButton; - loginFlowLayout.setVisibility(View.VISIBLE); + MaterialButton retryButton = accountSetupWebviewBinding.loginFlowV2.bRetry; - cancelButton.setOnClickListener(v -> { + retryButton.setOnClickListener(v -> { loginFlowExecutorService.shutdown(); ProcessLifecycleOwner.get().getLifecycle().removeObserver(lifecycleEventObserver); recreate(); @@ -1387,13 +1399,20 @@ public void onAuthenticatorTaskCallback(RemoteOperationResult result) } } + @IonosCustomization private void endSuccess() { if (onlyAdd) { finish(); } else { Intent i = new Intent(this, FileDisplayActivity.class); i.setAction(FileDisplayActivity.RESTART); - i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + String accountName = accountManager.getCurrentOwnCloudAccount() != null + ? accountManager.getCurrentOwnCloudAccount().getName() + : null; + if (!privacyPreferences.isDataProtectionProcessed(accountName)) { + i = DataProtectionActivity.createIntent(this, i); + } + i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); startActivity(i); } } diff --git a/app/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java b/app/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java index 879551ef7ac3..c4ec60259b45 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java @@ -34,6 +34,7 @@ import android.widget.FrameLayout; import android.widget.ImageView; +import com.ionos.annotation.IonosCustomization; import com.nextcloud.client.account.User; import com.nextcloud.client.network.ConnectivityService; import com.owncloud.android.MainApp; @@ -608,6 +609,7 @@ protected Bitmap doInBackground(ThumbnailGenerationTaskObject... params) { return thumbnail; } + @IonosCustomization protected void onPostExecute(Bitmap bitmap) { if (bitmap != null && mImageViewReference != null) { final ImageView imageView = mImageViewReference.get(); @@ -623,11 +625,7 @@ protected void onPostExecute(Bitmap bitmap) { tagId = String.valueOf(((TrashbinFile) mFile).getRemoteId()); } if (String.valueOf(imageView.getTag()).equals(tagId)) { - if (gridViewEnabled) { - BitmapUtils.setRoundedBitmapForGridMode(bitmap, imageView); - } else { - BitmapUtils.setRoundedBitmap(bitmap, imageView); - } + imageView.setImageBitmap(bitmap); } } diff --git a/app/src/main/java/com/owncloud/android/media/MediaControlView.kt b/app/src/main/java/com/owncloud/android/media/MediaControlView.kt index 25747c3f1bd0..85a10f78cd58 100644 --- a/app/src/main/java/com/owncloud/android/media/MediaControlView.kt +++ b/app/src/main/java/com/owncloud/android/media/MediaControlView.kt @@ -22,11 +22,14 @@ import android.view.LayoutInflater import android.view.View import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityNodeInfo +import android.widget.ImageButton import android.widget.LinearLayout import android.widget.MediaController.MediaPlayerControl import android.widget.SeekBar import android.widget.SeekBar.OnSeekBarChangeListener import androidx.core.content.ContextCompat +import com.google.android.material.button.MaterialButton +import com.ionos.annotation.IonosCustomization import com.owncloud.android.MainApp import com.owncloud.android.R import com.owncloud.android.databinding.MediaControlBinding @@ -77,6 +80,7 @@ class MediaControlView(context: Context, attrs: AttributeSet?) : } @Suppress("MagicNumber") + @IonosCustomization("Removed theming") private fun initControllerView() { binding.playBtn.requestFocus() @@ -85,17 +89,10 @@ class MediaControlView(context: Context, attrs: AttributeSet?) : binding.rewindBtn.setOnClickListener(this) binding.progressBar.run { - viewThemeUtils.platform.themeHorizontalSeekBar(this) setMax(1000) } binding.progressBar.setOnSeekBarChangeListener(this) - - viewThemeUtils.material.run { - colorMaterialButtonPrimaryTonal(binding.rewindBtn) - colorMaterialButtonPrimaryTonal(binding.playBtn) - colorMaterialButtonPrimaryTonal(binding.forwardBtn) - } } /** @@ -222,15 +219,25 @@ class MediaControlView(context: Context, attrs: AttributeSet?) : } } + @IonosCustomization("ImageButton support") fun updatePausePlay() { - binding.playBtn.icon = ContextCompat.getDrawable( - context, + val iconResource = if (playerControl?.isPlaying == true) { R.drawable.ic_pause } else { R.drawable.ic_play } - ) + val playBtn: View = binding.playBtn + + if (playBtn is MaterialButton){ + playBtn.icon = ContextCompat.getDrawable( + context, + iconResource + ) + } else if(playBtn is ImageButton){ + playBtn.setImageResource(iconResource) + } + binding.forwardBtn.visibility = if (playerControl?.canSeekForward() == true) { VISIBLE } else { @@ -266,6 +273,7 @@ class MediaControlView(context: Context, attrs: AttributeSet?) : } @Suppress("MagicNumber") + @IonosCustomization("changed forward to 5 sec") override fun onClick(v: View) { var pos: Int @@ -288,7 +296,7 @@ class MediaControlView(context: Context, attrs: AttributeSet?) : } R.id.forwardBtn -> { pos = playerControl.currentPosition - pos += 15000 + pos += 5000 playerControl.seekTo(pos) if (!playing) { diff --git a/app/src/main/java/com/owncloud/android/ui/RoundedCornersConstraintLayout.kt b/app/src/main/java/com/owncloud/android/ui/RoundedCornersConstraintLayout.kt new file mode 100644 index 000000000000..6574db7cdfed --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/RoundedCornersConstraintLayout.kt @@ -0,0 +1,25 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.owncloud.android.ui + +import android.content.Context +import android.util.AttributeSet +import androidx.constraintlayout.widget.ConstraintLayout +import com.owncloud.android.R + +class RoundedCornersConstraintLayout @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, +) : ConstraintLayout(context, attrs, defStyleAttr) { + + init { + setBackgroundResource(R.drawable.rounded_rect_4dp) + clipToOutline = true + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/SquareFrameLayout.kt b/app/src/main/java/com/owncloud/android/ui/SquareFrameLayout.kt new file mode 100644 index 000000000000..4f12f95adcb0 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/SquareFrameLayout.kt @@ -0,0 +1,23 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.owncloud.android.ui + +import android.content.Context +import android.util.AttributeSet +import android.widget.FrameLayout + +class SquareFrameLayout @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, +) : FrameLayout(context, attrs, defStyleAttr) { + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + super.onMeasure(widthMeasureSpec, widthMeasureSpec) + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/AnimatedDrawerListener.java b/app/src/main/java/com/owncloud/android/ui/activity/AnimatedDrawerListener.java new file mode 100644 index 000000000000..545040e72c31 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/activity/AnimatedDrawerListener.java @@ -0,0 +1,80 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO AG. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.owncloud.android.ui.activity; + +import android.animation.ArgbEvaluator; +import android.animation.ValueAnimator; +import android.app.Activity; +import android.view.View; + +import com.owncloud.android.R; +import com.owncloud.android.utils.theme.ViewThemeUtils; + +import androidx.annotation.StringRes; +import androidx.appcompat.app.ActionBarDrawerToggle; +import androidx.drawerlayout.widget.DrawerLayout; + +class AnimatedDrawerListener extends ActionBarDrawerToggle { + private static final float CHANGE_GAIN = 0.1f; + + private final Activity activity; + private final ViewThemeUtils viewThemeUtils; + private final ValueAnimator valueAnimator; + + AnimatedDrawerListener(Activity activity, + DrawerLayout drawerLayout, + @StringRes int openDrawerContentDescRes, + @StringRes int closeDrawerContentDescRes, + ViewThemeUtils viewThemeUtils + ) { + super(activity, drawerLayout, openDrawerContentDescRes, closeDrawerContentDescRes); + + this.activity = activity; + this.viewThemeUtils = viewThemeUtils; + this.valueAnimator = createValueAnimator(); + } + + private ValueAnimator createValueAnimator() { + int colorFrom = this.activity.getResources().getColor(R.color.bg_default); + int colorTo = getOpenedDrawerColor(); + return ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo); + } + + @Override + public void onDrawerSlide(View drawerView, float slideOffset) { + super.onDrawerSlide(drawerView, slideOffset); + + if (shouldUpdateSystemBarColor(slideOffset)) { + this.valueAnimator.setCurrentFraction(slideOffset); + this.viewThemeUtils.ionos.platform.themeSystemBars(this.activity, (int) this.valueAnimator.getAnimatedValue()); + } + } + + @Override + public void onDrawerOpened(View drawerView) { + super.onDrawerOpened(drawerView); + + this.viewThemeUtils.ionos.platform.themeSystemBars(this.activity, getOpenedDrawerColor()); + } + + @Override + public void onDrawerClosed(View drawerView) { + super.onDrawerClosed(drawerView); + + this.viewThemeUtils.ionos.platform.themeSystemBars(this.activity); + } + + private boolean shouldUpdateSystemBarColor(float slideOffset) { + float delta = Math.abs(slideOffset - this.valueAnimator.getAnimatedFraction()); + return delta > CHANGE_GAIN; + } + + private int getOpenedDrawerColor() { + return this.activity.getResources().getColor(R.color.drawer_header_background); + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index 42991b80590e..d545018164f5 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -49,11 +49,12 @@ import com.google.android.material.button.MaterialButton; import com.google.android.material.navigation.NavigationView; import com.google.android.material.progressindicator.LinearProgressIndicator; +import com.ionos.annotation.IonosCustomization; +import com.ionos.authorization_method.AuthorizationMethodActivity; import com.nextcloud.client.account.User; import com.nextcloud.client.di.Injectable; import com.nextcloud.client.files.DeepLinkConstants; import com.nextcloud.client.network.ClientFactory; -import com.nextcloud.client.onboarding.FirstRunActivity; import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.common.NextcloudClient; import com.nextcloud.ui.ChooseAccountDialogFragment; @@ -117,8 +118,8 @@ import androidx.annotation.IdRes; import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBarDrawerToggle; +import androidx.appcompat.content.res.AppCompatResources; import androidx.core.content.ContextCompat; -import androidx.core.content.res.ResourcesCompat; import androidx.core.view.GravityCompat; import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.Fragment; @@ -239,8 +240,9 @@ protected void setupDrawer() { /** * initializes and sets up the drawer toggle. */ + @IonosCustomization private void setupDrawerToggle() { - mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.string.drawer_open, R.string.drawer_close) { + mDrawerToggle = new AnimatedDrawerListener(this, mDrawerLayout, R.string.drawer_open, R.string.drawer_close, viewThemeUtils) { /** Called when a drawer has settled in a completely closed state. */ public void onDrawerClosed(View view) { @@ -268,11 +270,9 @@ public void onDrawerOpened(View drawerView) { mDrawerLayout.addDrawerListener(mDrawerToggle); mDrawerToggle.setDrawerIndicatorEnabled(true); mDrawerToggle.setDrawerSlideAnimationEnabled(true); - Drawable backArrow = ResourcesCompat.getDrawable(getResources(), - R.drawable.ic_arrow_back, - null); - - viewThemeUtils.platform.tintToolbarArrowDrawable(this, mDrawerToggle, backArrow); + Drawable drawerIndicator = AppCompatResources.getDrawable(this, R.drawable.ic_menu); + mDrawerToggle.setDrawerArrowDrawable(new SingleStateDrawerArrowDrawable(this, drawerIndicator)); + mDrawerToggle.setHomeAsUpIndicator(R.drawable.ic_arrow_back); } /** @@ -479,6 +479,8 @@ private void filterDrawerMenu(final Menu menu, @NonNull final User user) { DrawerMenuUtil.removeMenuItem(menu, R.id.nav_community, !getResources().getBoolean(R.bool.participate_enabled)); DrawerMenuUtil.removeMenuItem(menu, R.id.nav_shared, !getResources().getBoolean(R.bool.shared_enabled)); DrawerMenuUtil.removeMenuItem(menu, R.id.nav_logout, !getResources().getBoolean(R.bool.show_drawer_logout)); + DrawerMenuUtil.removePersonalFiles(menu); + DrawerMenuUtil.removeNotifications(menu); } @Subscribe(threadMode = ThreadMode.MAIN) @@ -596,13 +598,13 @@ public void openManageAccounts() { startActivityForResult(manageAccountsIntent, ACTION_MANAGE_ACCOUNTS); } + @IonosCustomization public void openAddAccount() { boolean isProviderOrOwnInstallationVisible = getResources() .getBoolean(R.bool.show_provider_or_own_installation); if (isProviderOrOwnInstallationVisible) { - Intent firstRunIntent = new Intent(getApplicationContext(), FirstRunActivity.class); - firstRunIntent.putExtra(FirstRunActivity.EXTRA_ALLOW_CLOSE, true); + Intent firstRunIntent = AuthorizationMethodActivity.createInstance(getApplicationContext()); startActivity(firstRunIntent); } else { startAccountCreation(); @@ -781,6 +783,7 @@ private void showQuota(boolean showQuota) { * @param relative the percentage of space already used * @param quotaValue {@link GetUserInfoRemoteOperation#SPACE_UNLIMITED} or other to determinate state */ + @IonosCustomization private void setQuotaInformation(long usedSpace, long totalSpace, int relative, long quotaValue) { if (GetUserInfoRemoteOperation.SPACE_UNLIMITED == quotaValue) { mQuotaTextPercentage.setText(String.format( @@ -796,10 +799,9 @@ private void setQuotaInformation(long usedSpace, long totalSpace, int relative, mQuotaProgressBar.setProgress(relative); if (relative < RELATIVE_THRESHOLD_WARNING) { - viewThemeUtils.material.colorProgressBar(mQuotaProgressBar); + mQuotaProgressBar.setIndicatorColor(getResources().getColor(R.color.sidemenu_progress_bar_color, getTheme())); } else { - viewThemeUtils.material.colorProgressBar(mQuotaProgressBar, - getResources().getColor(R.color.infolevel_warning, getTheme())); + mQuotaProgressBar.setIndicatorColor(getResources().getColor(R.color.sidemenu_warn_progress_bar_color, getTheme())); } updateQuotaLink(); @@ -881,9 +883,9 @@ public void onLoadFailed(Exception e, Drawable errorDrawable) { * * @param menuItemId the menu item to be highlighted */ + @IonosCustomization("Removed server styles for the drawer") protected void setDrawerMenuItemChecked(int menuItemId) { if (mNavigationView != null && mNavigationView.getMenu().findItem(menuItemId) != null) { - viewThemeUtils.platform.colorNavigationView(mNavigationView); mCheckedMenuItem = menuItemId; mNavigationView.getMenu().findItem(menuItemId).setChecked(true); } else { diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ExternalSiteWebView.java b/app/src/main/java/com/owncloud/android/ui/activity/ExternalSiteWebView.java index fb917e19d553..b4f9bfd10939 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ExternalSiteWebView.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/ExternalSiteWebView.java @@ -22,6 +22,7 @@ import android.webkit.WebView; import android.widget.ProgressBar; +import com.ionos.annotation.IonosCustomization; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.databinding.ExternalsiteWebviewBinding; @@ -56,6 +57,7 @@ public class ExternalSiteWebView extends FileActivity { String url; @Override + @IonosCustomization protected final void onCreate(Bundle savedInstanceState) { Log_OC.v(TAG, "onCreate() start"); bindView(); @@ -81,6 +83,8 @@ protected final void onCreate(Bundle savedInstanceState) { setContentView(getRootView()); postOnCreate(); + + viewThemeUtils.ionos.platform.themeSystemBars(this); } protected void postOnCreate() { diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java index 58bf035eec35..5a77df42960e 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileActivity.java @@ -30,6 +30,7 @@ import android.text.TextUtils; import com.google.android.material.snackbar.Snackbar; +import com.ionos.annotation.IonosCustomization; import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.jobs.BackgroundJobManager; @@ -442,6 +443,7 @@ protected void requestCredentialsUpdate(Context context, Account account) { } } + @IonosCustomization public void performCredentialsUpdate(Account account, Context context) { try { /// step 1 - invalidate credentials of current account @@ -467,6 +469,8 @@ public void performCredentialsUpdate(Account account, Context context) { AuthenticatorActivity.EXTRA_ACTION, AuthenticatorActivity.ACTION_UPDATE_EXPIRED_TOKEN); updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); + updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); + updateAccountCredentials.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivityForResult(updateAccountCredentials, REQUEST_CODE__UPDATE_CREDENTIALS); } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException e) { DisplayUtils.showSnackMessage(this, R.string.auth_account_does_not_exist); diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index 2b64bce92d3a..36dcd84e6cc2 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -47,6 +47,7 @@ import com.google.android.material.appbar.AppBarLayout; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.snackbar.Snackbar; +import com.ionos.annotation.IonosCustomization; import com.nextcloud.appReview.InAppReviewHelper; import com.nextcloud.client.account.User; import com.nextcloud.client.appinfo.AppInfo; @@ -797,6 +798,7 @@ public boolean onPrepareOptionsMenu(Menu menu) { } @Override + @IonosCustomization public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.activity_file_display, menu); @@ -810,8 +812,6 @@ public boolean onCreateOptionsMenu(Menu menu) { searchView.setIconified(false); }); - viewThemeUtils.androidx.themeToolbarSearchView(searchView); - // populate list of menu items to show/hide when drawer is opened/closed mDrawerMenuItemstoShowHideList = new ArrayList<>(1); mDrawerMenuItemstoShowHideList.add(searchMenuItem); @@ -1413,6 +1413,7 @@ private boolean checkForRemoteOperationError(RemoteOperationResult syncResult) { /** * Show a text message on screen view for notifying user if content is loading or folder is empty */ + @IonosCustomization private void setBackgroundText() { final OCFileListFragment ocFileListFragment = getListOfFilesFragment(); if (ocFileListFragment != null) { @@ -1420,7 +1421,7 @@ private void setBackgroundText() { ocFileListFragment.setEmptyListLoadingMessage(); } else { if (MainApp.isOnlyOnDevice()) { - ocFileListFragment.setMessageForEmptyList(R.string.file_list_empty_headline, R.string.file_list_empty_on_device, R.drawable.ic_list_empty_folder, true); + ocFileListFragment.setMessageForEmptyList(R.string.file_list_empty_headline, R.string.file_list_empty_on_device, R.drawable.ic_list_empty_folder); } else { ocFileListFragment.setEmptyListMessage(SearchType.NO_SEARCH); } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt index e3cab8ab92b5..24fa83c18b14 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt @@ -22,6 +22,7 @@ import android.view.MenuItem import android.view.View import androidx.activity.OnBackPressedCallback import androidx.localbroadcastmanager.content.LocalBroadcastManager +import com.ionos.annotation.IonosCustomization import com.nextcloud.client.di.Injectable import com.nextcloud.utils.fileNameValidator.FileNameValidator import com.owncloud.android.R @@ -206,6 +207,7 @@ open class FolderPickerActivity : /** * Show a text message on screen view for notifying user if content is loading or folder is empty */ + @IonosCustomization private fun setBackgroundText() { val listFragment = listOfFilesFragment @@ -219,7 +221,6 @@ open class FolderPickerActivity : R.string.folder_list_empty_headline, R.string.file_list_empty_moving, R.drawable.ic_list_empty_create_folder, - true ) } else { it.setEmptyListLoadingMessage() diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java index aa4f032a6660..1c447fc760df 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java @@ -23,11 +23,12 @@ import android.view.View; import com.google.common.collect.Sets; +import com.ionos.annotation.IonosCustomization; +import com.ionos.authorization_method.AuthorizationMethodActivity; import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.jobs.BackgroundJobManager; import com.nextcloud.client.jobs.download.FileDownloadHelper; -import com.nextcloud.client.onboarding.FirstRunActivity; import com.nextcloud.model.WorkerState; import com.nextcloud.model.WorkerStateLiveData; import com.nextcloud.utils.extensions.BundleExtensionsKt; @@ -252,9 +253,9 @@ public boolean onOptionsItemSelected(MenuItem item) { } @Override + @IonosCustomization public void showFirstRunActivity() { - Intent firstRunIntent = new Intent(getApplicationContext(), FirstRunActivity.class); - firstRunIntent.putExtra(FirstRunActivity.EXTRA_ALLOW_CLOSE, true); + Intent firstRunIntent = AuthorizationMethodActivity.createInstance(getApplicationContext()); startActivity(firstRunIntent); } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java index 27f5237dc710..81e5f64fb32a 100755 --- a/app/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java @@ -47,7 +47,11 @@ import com.google.android.material.button.MaterialButton; import com.google.android.material.dialog.MaterialAlertDialogBuilder; +import com.ionos.annotation.IonosCustomization; +import com.ionos.privacy.DataProtectionActivity; +import com.ionos.privacy.PrivacyPreferences; import com.nextcloud.client.account.User; +import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.di.Injectable; import com.nextcloud.client.jobs.upload.FileUploadHelper; import com.nextcloud.client.jobs.upload.FileUploadWorker; @@ -134,6 +138,7 @@ public class ReceiveExternalFilesActivity extends FileActivity public static final int SINGLE_PARENT = 1; @Inject AppPreferences preferences; + @Inject PrivacyPreferences privacyPreferences; @Inject LocalBroadcastManager localBroadcastManager; @Inject SyncedFolderProvider syncedFolderProvider; @@ -166,6 +171,7 @@ public class ReceiveExternalFilesActivity extends FileActivity private ReceiveExternalFilesBinding binding; @Override + @IonosCustomization protected void onCreate(Bundle savedInstanceState) { if (savedInstanceState != null) { String parentPath = savedInstanceState.getString(KEY_PARENTS); @@ -179,6 +185,14 @@ protected void onCreate(Bundle savedInstanceState) { mAccountManager = (AccountManager) getSystemService(Context.ACCOUNT_SERVICE); super.onCreate(savedInstanceState); + + String accountName = accountManager.getCurrentOwnCloudAccount() != null + ? accountManager.getCurrentOwnCloudAccount().getName() + : null; + if (!privacyPreferences.isDataProtectionProcessed(accountName)) { + startActivity(DataProtectionActivity.createIntent(this)); + } + binding = ReceiveExternalFilesBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); @@ -287,9 +301,10 @@ protected void onDestroy() { } @Override + @IonosCustomization public void onSortingOrderChosen(FileSortOrder newSortOrder) { preferences.setSortOrder(mFile, newSortOrder); - sortButton.setText(DisplayUtils.getSortOrderStringId(newSortOrder)); + sortButton.setIconResource(DisplayUtils.getSortOrderIconRes(newSortOrder)); populateDirectoryList(); } @@ -727,6 +742,7 @@ private void setupActionBarSubtitle() { } } + @IonosCustomization private void populateDirectoryList() { setupEmptyList(); setupToolbar(); @@ -775,14 +791,14 @@ private void populateDirectoryList() { btnChooseFolder.setEnabled(mFile.canWrite()); - viewThemeUtils.platform.themeStatusBar(this); + viewThemeUtils.ionos.platform.themeSystemBars(this); viewThemeUtils.material.colorMaterialButtonPrimaryOutlined(binding.uploaderCancel); binding.uploaderCancel.setOnClickListener(this); sortButton = binding.toolbarLayout.sortButton; FileSortOrder sortOrder = preferences.getSortOrderByFolder(mFile); - sortButton.setText(DisplayUtils.getSortOrderStringId(sortOrder)); + sortButton.setIconResource(DisplayUtils.getSortOrderIconRes(sortOrder)); sortButton.setOnClickListener(l -> openSortingOrderDialogFragment(getSupportFragmentManager(), sortOrder)); } } @@ -1049,6 +1065,7 @@ public boolean onCreateOptionsMenu(Menu menu) { return true; } + @IonosCustomization private void setupSearchView(Menu menu) { final MenuItem searchMenuItem = menu.findItem(R.id.action_search); @@ -1066,8 +1083,6 @@ public boolean onQueryTextChange(String newText) { return false; } }); - - viewThemeUtils.androidx.themeToolbarSearchView(searchView); } @Override diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java index 84d5beaa26de..80905f6a8f31 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java @@ -39,6 +39,8 @@ import android.webkit.URLUtil; import com.google.android.material.dialog.MaterialAlertDialogBuilder; +import com.ionos.annotation.IonosCustomization; +import com.ionos.privacy.PrivacySettingsActivity; import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.di.Injectable; @@ -85,7 +87,6 @@ import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatDelegate; import androidx.core.content.ContextCompat; -import androidx.core.content.res.ResourcesCompat; /** * An Activity that allows the user to change the application's settings. @@ -142,11 +143,13 @@ public class SettingsActivity extends PreferenceActivity @SuppressWarnings("deprecation") @Override + @IonosCustomization("Delegate fix") public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getDelegate().installViewFactory(); getDelegate().onCreate(savedInstanceState); + + super.onCreate(savedInstanceState); + addPreferencesFromResource(R.xml.preferences); setupActionBar(); @@ -185,6 +188,9 @@ public void onCreate(Bundle savedInstanceState) { // workaround for mismatched color when app dark mode and system dark mode don't agree setListBackground(); + + // workaround to set custom paddings + setListPadding(); } private void setupDevCategory(PreferenceScreen preferenceScreen) { @@ -319,9 +325,10 @@ private void setupSyncCategory() { viewThemeUtils.files.themePreferenceCategory(preferenceCategorySync); setupAutoUploadPreference(preferenceCategorySync); - setupInternalTwoWaySyncPreference(); + setupInternalTwoWaySyncPreference(preferenceCategorySync); } + @IonosCustomization private void setupMoreCategory() { final PreferenceCategory preferenceCategoryMore = (PreferenceCategory) findPreference("more"); viewThemeUtils.files.themePreferenceCategory(preferenceCategoryMore); @@ -338,6 +345,8 @@ private void setupMoreCategory() { removeE2E(preferenceCategoryMore); + setupPrivacySettingsPreference(preferenceCategoryMore); + setupHelpPreference(preferenceCategoryMore); setupRecommendPreference(preferenceCategoryMore); @@ -388,6 +397,7 @@ private void setupLoggingPreference(PreferenceCategory preferenceCategoryMore) { } + @IonosCustomization private void setupRecommendPreference(PreferenceCategory preferenceCategoryMore) { boolean recommendEnabled = getResources().getBoolean(R.bool.recommend_enabled); Preference pRecommend = findPreference("recommend"); @@ -402,8 +412,13 @@ private void setupRecommendPreference(PreferenceCategory preferenceCategoryMore) String appName = getString(R.string.app_name); String downloadUrlGooglePlayStore = getString(R.string.url_app_download); String downloadUrlFDroid = getString(R.string.fdroid_link); - String downloadUrls = String.format(getString(R.string.recommend_urls), - downloadUrlGooglePlayStore, downloadUrlFDroid); + String downloadUrls; + if (URLUtil.isValidUrl(downloadUrlFDroid)) { + downloadUrls = String.format(getString(R.string.recommend_urls), + downloadUrlGooglePlayStore, downloadUrlFDroid); + } else { + downloadUrls = downloadUrlGooglePlayStore; + } String recommendSubject = String.format(getString(R.string.recommend_subject), appName); String recommendText = String.format(getString(R.string.recommend_text), @@ -530,6 +545,16 @@ private void showRemoveE2EAlertDialog(PreferenceCategory preferenceCategoryMore, .show(); } + private void setupPrivacySettingsPreference(PreferenceCategory preferenceCategoryMore) { + Preference privacySettings = findPreference("privacy_settings"); + if (privacySettings != null) { + privacySettings.setOnPreferenceClickListener(preference -> { + startActivity(PrivacySettingsActivity.createIntent(this)); + return true; + }); + } + } + private void setupHelpPreference(PreferenceCategory preferenceCategoryMore) { boolean helpEnabled = getResources().getBoolean(R.bool.help_enabled); Preference pHelp = findPreference("help"); @@ -557,15 +582,11 @@ private void setupAutoUploadPreference(PreferenceCategory preferenceCategoryMore }); } } - - private void setupInternalTwoWaySyncPreference() { + + @IonosCustomization("internal_two_way_sync was hidden") + private void setupInternalTwoWaySyncPreference(PreferenceCategory preferenceCategorySync) { Preference twoWaySync = findPreference("internal_two_way_sync"); - - twoWaySync.setOnPreferenceClickListener(preference -> { - Intent intent = new Intent(this, InternalTwoWaySyncActivity.class); - startActivity(intent); - return true; - }); + preferenceCategorySync.removePreference(twoWaySync); } private void setupBackupPreference() { @@ -789,12 +810,14 @@ private void disableLock(String lock) { } } + @IonosCustomization("Workaround to hide prefStoragePath") private void setupGeneralCategory() { final PreferenceCategory preferenceCategoryGeneral = (PreferenceCategory) findPreference("general"); viewThemeUtils.files.themePreferenceCategory(preferenceCategoryGeneral); prefStoragePath = (ListPreference) findPreference(AppPreferencesImpl.STORAGE_PATH); if (prefStoragePath != null) { + preferenceCategoryGeneral.removePreference(prefStoragePath); StoragePoint[] storageOptions = DataStorageProvider.getInstance().getAvailableStoragePoints(); String[] entries = new String[storageOptions.length]; String[] values = new String[storageOptions.length]; @@ -855,6 +878,12 @@ private void setListBackground() { getListView().setBackgroundColor(ContextCompat.getColor(this, R.color.bg_default)); } + @IonosCustomization + private void setListPadding() { + int bottom = (int) getResources().getDimension(R.dimen.settings_screen_list_bottom_padding); + getListView().setPadding(0, 0, 0, bottom); + } + private String getAppVersion() { String temp; try { @@ -873,21 +902,16 @@ public boolean onOptionsItemSelected(MenuItem item) { return super.onOptionsItemSelected(item); } + @IonosCustomization private void setupActionBar() { ActionBar actionBar = getDelegate().getSupportActionBar(); if (actionBar != null) { - viewThemeUtils.platform.themeStatusBar(this); + viewThemeUtils.ionos.platform.themeSystemBars(this); actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setDisplayShowTitleEnabled(true); - if (this.getResources() != null) { - viewThemeUtils.androidx.themeActionBar(this, - actionBar, - getString(R.string.actionbar_settings), - ResourcesCompat.getDrawable(this.getResources(), - R.drawable.ic_arrow_back, - null)); - } + actionBar.setHomeAsUpIndicator(R.drawable.ic_arrow_back); + actionBar.setTitle(R.string.actionbar_settings); } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SingleStateDrawerArrowDrawable.kt b/app/src/main/java/com/owncloud/android/ui/activity/SingleStateDrawerArrowDrawable.kt new file mode 100644 index 000000000000..60ed8da431ed --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/activity/SingleStateDrawerArrowDrawable.kt @@ -0,0 +1,24 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.owncloud.android.ui.activity + +import android.content.Context +import android.graphics.Canvas +import android.graphics.drawable.Drawable +import androidx.appcompat.graphics.drawable.DrawerArrowDrawable + +class SingleStateDrawerArrowDrawable( + context: Context, + private val drawable: Drawable, +) : DrawerArrowDrawable(context) { + + override fun draw(canvas: Canvas) { + drawable.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight) + drawable.draw(canvas) + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt index 06a7267adf04..bd6991ce1bd0 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt @@ -25,6 +25,7 @@ import androidx.drawerlayout.widget.DrawerLayout import androidx.lifecycle.Lifecycle import androidx.recyclerview.widget.GridLayoutManager import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.ionos.annotation.IonosCustomization import com.nextcloud.client.core.Clock import com.nextcloud.client.device.PowerManagementService import com.nextcloud.client.di.Injectable @@ -804,6 +805,8 @@ class SyncedFoldersActivity : item.setExcludeHidden(excludeHidden) } + @IonosCustomization("StackOverflow fix") + private var externalStoragePermissionRequestCount = 0 override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { when (requestCode) { PermissionUtil.PERMISSIONS_EXTERNAL_STORAGE -> { @@ -811,12 +814,15 @@ class SyncedFoldersActivity : if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // permission was granted load(getItemsDisplayedPerFolder(), true) + } else if (externalStoragePermissionRequestCount++ == 0) { + PermissionUtil.requestExternalStoragePermission(this, viewThemeUtils, true) } } else -> super.onRequestPermissionsResult(requestCode, permissions, grantResults) } } + @IonosCustomization("Buttons style") private fun showBatteryOptimizationInfo() { if (powerManagementService.isPowerSavingExclusionAvailable || checkIfBatteryOptimizationEnabled()) { val alertDialogBuilder = MaterialAlertDialogBuilder(this, R.style.Theme_ownCloud_Dialog) @@ -837,7 +843,7 @@ class SyncedFoldersActivity : .setIcon(R.drawable.ic_battery_alert) if (lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED)) { val alertDialog = alertDialogBuilder.show() - viewThemeUtils.platform.colorTextButtons( + viewThemeUtils.ionos.platform.colorTextButtons( alertDialog.getButton(AlertDialog.BUTTON_POSITIVE), alertDialog.getButton(AlertDialog.BUTTON_NEUTRAL) ) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java index 14f8ba64a26c..58b647394e3d 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/ToolbarActivity.java @@ -13,7 +13,6 @@ */ package com.owncloud.android.ui.activity; -import android.animation.AnimatorInflater; import android.annotation.SuppressLint; import android.graphics.Bitmap; import android.graphics.Color; @@ -30,6 +29,7 @@ import com.google.android.material.button.MaterialButton; import com.google.android.material.card.MaterialCardView; import com.google.android.material.textview.MaterialTextView; +import com.ionos.annotation.IonosCustomization; import com.nextcloud.client.di.Injectable; import com.owncloud.android.R; import com.owncloud.android.datamodel.FileDataStorageManager; @@ -72,6 +72,7 @@ public abstract class ToolbarActivity extends BaseActivity implements Injectable * Toolbar setup that must be called in implementer's {@link #onCreate} after {@link #setContentView} if they want * to use the toolbar. */ + @IonosCustomization private void setupToolbar(boolean isHomeSearchToolbarShow, boolean showSortListButtonGroup) { mToolbar = findViewById(R.id.toolbar); setSupportActionBar(mToolbar); @@ -98,9 +99,7 @@ private void setupToolbar(boolean isHomeSearchToolbarShow, boolean showSortListB mToolbarSpinner = findViewById(R.id.toolbar_spinner); - viewThemeUtils.material.themeToolbar(mToolbar); - viewThemeUtils.material.colorToolbarOverflowIcon(mToolbar); - viewThemeUtils.platform.themeStatusBar(this); + viewThemeUtils.ionos.platform.themeSystemBars(this); viewThemeUtils.material.colorMaterialTextButton(mSwitchAccountButton); } @@ -155,26 +154,21 @@ public void hideSearchView(OCFile chosenFile) { } } + @IonosCustomization private void showHomeSearchToolbar(String title, boolean isRoot) { showHomeSearchToolbar(isHomeSearchToolbarShow && isRoot); - mSearchText.setText(getString(R.string.appbar_search_in, title)); + mSearchText.setText(getString(R.string.actionbar_search, title)); } @SuppressLint("PrivateResource") + @IonosCustomization private void showHomeSearchToolbar(boolean isShow) { - viewThemeUtils.material.themeToolbar(mToolbar); if (isShow) { - viewThemeUtils.platform.resetStatusBar(this); - mAppBar.setStateListAnimator(AnimatorInflater.loadStateListAnimator(mAppBar.getContext(), - R.animator.appbar_elevation_off)); + viewThemeUtils.ionos.platform.resetSystemBars(this); mDefaultToolbar.setVisibility(View.GONE); mHomeSearchToolbar.setVisibility(View.VISIBLE); - viewThemeUtils.material.themeCardView(mHomeSearchToolbar); - viewThemeUtils.material.themeSearchBarText(mSearchText); } else { - mAppBar.setStateListAnimator(AnimatorInflater.loadStateListAnimator(mAppBar.getContext(), - R.animator.appbar_elevation_on)); - viewThemeUtils.platform.themeStatusBar(this); + viewThemeUtils.ionos.platform.themeSystemBars(this); mDefaultToolbar.setVisibility(View.VISIBLE); mHomeSearchToolbar.setVisibility(View.GONE); } @@ -284,11 +278,11 @@ public FrameLayout getPreviewImageContainer() { return mPreviewImageContainer; } + @IonosCustomization public void updateToolbarSubtitle(@NonNull String subtitle) { ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { actionBar.setSubtitle(subtitle); - viewThemeUtils.androidx.themeActionBarSubtitle(this, actionBar); } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java index 09f537f66f31..6b3754a8352e 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java @@ -28,6 +28,7 @@ import android.widget.ArrayAdapter; import android.widget.TextView; +import com.ionos.annotation.IonosCustomization; import com.nextcloud.client.account.User; import com.nextcloud.client.di.Injectable; import com.nextcloud.client.jobs.upload.FileUploadHelper; @@ -137,6 +138,7 @@ public static void startUploadActivityForResult(Activity activity, @Override @SuppressLint("WrongViewCast") // wrong error on finding local_files_list + @IonosCustomization public void onCreate(Bundle savedInstanceState) { Log_OC.d(TAG, "onCreate() start"); super.onCreate(savedInstanceState); @@ -172,7 +174,7 @@ public void onCreate(Bundle savedInstanceState) { /// USER INTERFACE // Drop-down navigation - mDirectories = new ArrayAdapter<>(this, android.R.layout.simple_spinner_item); + mDirectories = new ArrayAdapter<>(this, R.layout.simple_spinner_item); mDirectories.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); fillDirectoryDropdown(); @@ -280,6 +282,7 @@ private void fillDirectoryDropdown() { } @Override + @IonosCustomization public boolean onCreateOptionsMenu(Menu menu) { mOptionsMenu = menu; getMenuInflater().inflate(R.menu.activity_upload_files, menu); @@ -291,8 +294,6 @@ public boolean onCreateOptionsMenu(Menu menu) { final MenuItem item = menu.findItem(R.id.action_search); mSearchView = (SearchView) MenuItemCompat.getActionView(item); - viewThemeUtils.androidx.themeToolbarSearchView(mSearchView); - viewThemeUtils.platform.tintTextDrawable(this, menu.findItem(R.id.action_choose_storage_path).getIcon()); mSearchView.setOnSearchClickListener(v -> mToolbarSpinner.setVisibility(View.GONE)); diff --git a/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java index 7543267ad3ed..68a920a364cc 100755 --- a/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java @@ -20,6 +20,7 @@ import android.view.MenuItem; import android.view.View; +import com.ionos.annotation.IonosCustomization; import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.core.Clock; @@ -151,12 +152,11 @@ private void handleUploadWorkerState() { uploadListAdapter.loadUploadItemsFromDb(); } + @IonosCustomization private void setupContent() { binding.list.setEmptyView(binding.emptyList.getRoot()); binding.emptyList.getRoot().setVisibility(View.GONE); binding.emptyList.emptyListIcon.setImageResource(R.drawable.uploads); - binding.emptyList.emptyListIcon.getDrawable().mutate(); - binding.emptyList.emptyListIcon.setAlpha(0.5f); binding.emptyList.emptyListIcon.setVisibility(View.VISIBLE); binding.emptyList.emptyListViewHeadline.setText(getString(R.string.upload_list_empty_headline)); binding.emptyList.emptyListViewText.setText(getString(R.string.upload_list_empty_text_auto_upload)); diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/FileDetailTabAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/FileDetailTabAdapter.java index 3c20828d1950..1fc577b6f1b2 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/FileDetailTabAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/FileDetailTabAdapter.java @@ -6,12 +6,12 @@ */ package com.owncloud.android.ui.adapter; +import com.ionos.annotation.IonosCustomization; import com.nextcloud.client.account.User; import com.nextcloud.ui.ImageDetailFragment; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.ui.fragment.FileDetailActivitiesFragment; import com.owncloud.android.ui.fragment.FileDetailSharingFragment; -import com.owncloud.android.utils.MimeTypeUtil; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; @@ -54,35 +54,16 @@ public ImageDetailFragment getImageDetailFragment() { @NonNull @Override + @IonosCustomization("Hide tabs in IONOS") public Fragment createFragment(int position) { - return switch (position) { - default -> { - fileDetailActivitiesFragment = FileDetailActivitiesFragment.newInstance(file, user); - yield fileDetailActivitiesFragment; - } - case 1 -> { - fileDetailSharingFragment = FileDetailSharingFragment.newInstance(file, user); - yield fileDetailSharingFragment; - } - case 2 -> { - imageDetailFragment = ImageDetailFragment.newInstance(file, user); - yield imageDetailFragment; - } - }; + fileDetailSharingFragment = FileDetailSharingFragment.newInstance(file, user); + return fileDetailSharingFragment; } @Override + @IonosCustomization("Hide tabs in IONOS") public int getItemCount() { - if (showSharingTab) { - if (MimeTypeUtil.isImage(file)) { - return 3; - } - return 2; - } else { - if (MimeTypeUtil.isImage(file)) { - return 2; - } - return 1; - } + if (showSharingTab) return 1; + else return 0; } } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/GalleryRowHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/GalleryRowHolder.kt index 26aeb7bb4224..f892b178ad18 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/GalleryRowHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/GalleryRowHolder.kt @@ -7,22 +7,27 @@ */ package com.owncloud.android.ui.adapter +import android.view.LayoutInflater +import android.view.View.INVISIBLE +import android.view.View.VISIBLE import android.widget.ImageView -import android.widget.LinearLayout +import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.content.res.ResourcesCompat +import androidx.core.view.children import androidx.core.view.get import com.afollestad.sectionedrecyclerview.SectionedViewHolder import com.elyeproj.loaderviewlibrary.LoaderImageView +import com.ionos.annotation.IonosCustomization import com.owncloud.android.R import com.owncloud.android.databinding.GalleryRowBinding +import com.owncloud.android.databinding.IonosItemMediaBinding import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.datamodel.GalleryRow import com.owncloud.android.datamodel.OCFile import com.owncloud.android.datamodel.ThumbnailsCacheManager -import com.owncloud.android.lib.resources.files.model.ImageDimension import com.owncloud.android.utils.BitmapUtils -import com.owncloud.android.utils.DisplayUtils +@IonosCustomization("Simplified grid") class GalleryRowHolder( val binding: GalleryRowBinding, private val defaultThumbnailSize: Float, @@ -38,14 +43,20 @@ class GalleryRowHolder( currentRow = row // re-use existing ones - while (binding.rowLayout.childCount < row.files.size) { - val shimmer = LoaderImageView(context).apply { + while (binding.rowLayout.childCount < galleryAdapter.columns) { + val itemBinding = IonosItemMediaBinding.inflate( + LayoutInflater.from(context), + binding.rowLayout, + true, + ) + + itemBinding.shimmer.apply { setImageResource(R.drawable.background) resetLoader() invalidate() } - val imageView = ImageView(context).apply { + itemBinding.image.apply { setImageDrawable( ThumbnailsCacheManager.AsyncGalleryImageDrawable( context.resources, @@ -58,23 +69,19 @@ class GalleryRowHolder( ) ) } - - LinearLayout(context).apply { - addView(shimmer) - addView(imageView) - - binding.rowLayout.addView(this) - } } - if (binding.rowLayout.childCount > row.files.size) { + if (binding.rowLayout.childCount > galleryAdapter.columns) { binding.rowLayout.removeViewsInLayout(row.files.size - 1, (binding.rowLayout.childCount - row.files.size)) } - val shrinkRatio = computeShrinkRatio(row) + binding.rowLayout.children.take(row.files.size) + .forEach { it.visibility = VISIBLE } + binding.rowLayout.children.drop(row.files.size) + .forEach { it.visibility = INVISIBLE } for (indexedFile in row.files.withIndex()) { - adjustFile(indexedFile, shrinkRatio, row) + adjustFile(indexedFile, row) } } @@ -82,97 +89,31 @@ class GalleryRowHolder( bind(currentRow) } - @SuppressWarnings("MagicNumber", "ComplexMethod") - private fun computeShrinkRatio(row: GalleryRow): Float { - val screenWidth = - DisplayUtils.convertDpToPixel(context.resources.configuration.screenWidthDp.toFloat(), context) - .toFloat() - - if (row.files.size > 1) { - var newSummedWidth = 0f - for (file in row.files) { - // first adjust all thumbnails to max height - val thumbnail1 = file.imageDimension ?: ImageDimension(defaultThumbnailSize, defaultThumbnailSize) - - val height1 = thumbnail1.height - val width1 = thumbnail1.width - - val scaleFactor1 = row.getMaxHeight() / height1 - val newHeight1 = height1 * scaleFactor1 - val newWidth1 = width1 * scaleFactor1 - - file.imageDimension = ImageDimension(newWidth1, newHeight1) - - newSummedWidth += newWidth1 - } - - var c = 1f - // this ensures that files in last row are better visible, - // e.g. when 2 images are there, it uses 2/5 of screen - if (galleryAdapter.columns == 5) { - when (row.files.size) { - 2 -> { - c = 5 / 2f - } - 3 -> { - c = 4 / 3f - } - 4 -> { - c = 4 / 5f - } - 5 -> { - c = 1f - } - } - } - - return (screenWidth / c) / newSummedWidth - } else { - val thumbnail1 = row.files[0].imageDimension ?: ImageDimension(defaultThumbnailSize, defaultThumbnailSize) - return (screenWidth / galleryAdapter.columns) / thumbnail1.width - } - } - - private fun adjustFile(indexedFile: IndexedValue, shrinkRatio: Float, row: GalleryRow) { + private fun adjustFile(indexedFile: IndexedValue, row: GalleryRow) { val file = indexedFile.value val index = indexedFile.index - val adjustedHeight1 = ((file.imageDimension?.height ?: defaultThumbnailSize) * shrinkRatio).toInt() - val adjustedWidth1 = ((file.imageDimension?.width ?: defaultThumbnailSize) * shrinkRatio).toInt() - - // re-use existing one - val linearLayout = binding.rowLayout[index] as LinearLayout + val linearLayout = binding.rowLayout[index] as ConstraintLayout val shimmer = linearLayout[0] as LoaderImageView - val thumbnail = linearLayout[1] as ImageView - thumbnail.adjustViewBounds = true - thumbnail.scaleType = ImageView.ScaleType.FIT_CENTER - ocFileListDelegate.bindGalleryRowThumbnail( shimmer, thumbnail, file, this, - adjustedWidth1 + thumbnail.width ) - val params = LinearLayout.LayoutParams(adjustedWidth1, adjustedHeight1) - val zero = context.resources.getInteger(R.integer.zero) val margin = context.resources.getInteger(R.integer.small_margin) if (index < (row.files.size - 1)) { - params.setMargins(zero, zero, margin, margin) + (thumbnail.layoutParams as ConstraintLayout.LayoutParams).setMargins(zero, zero, margin, margin) + (shimmer.layoutParams as ConstraintLayout.LayoutParams).setMargins(zero, zero, margin, margin) } else { - params.setMargins(zero, zero, zero, margin) + (thumbnail.layoutParams as ConstraintLayout.LayoutParams).setMargins(zero, zero, zero, margin) + (shimmer.layoutParams as ConstraintLayout.LayoutParams).setMargins(zero, zero, zero, margin) } - - thumbnail.layoutParams = params - thumbnail.layoutParams.height = adjustedHeight1 - thumbnail.layoutParams.width = adjustedWidth1 - - shimmer.layoutParams = params - shimmer.layoutParams.height = adjustedHeight1 - shimmer.layoutParams.width = adjustedWidth1 } + } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/ListGridImageViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/ListGridImageViewHolder.kt index fad80ec0f094..04ed1e9f1189 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/ListGridImageViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/ListGridImageViewHolder.kt @@ -13,11 +13,14 @@ import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView import com.elyeproj.loaderviewlibrary.LoaderImageView +import com.ionos.annotation.IonosCustomization interface ListGridImageViewHolder { val thumbnail: ImageView fun showVideoOverlay() val shimmerThumbnail: LoaderImageView + @IonosCustomization + val fileIcon: ImageView val favorite: ImageView val localFileIndicator: ImageView val imageFileName: TextView? diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java index 16c032707a29..1fc7cacd66bb 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/LocalFileListAdapter.java @@ -21,6 +21,7 @@ import android.widget.LinearLayout; import android.widget.TextView; +import com.ionos.annotation.IonosCustomization; import com.nextcloud.android.common.ui.theme.utils.ColorRole; import com.nextcloud.client.preferences.AppPreferences; import com.owncloud.android.R; @@ -157,6 +158,7 @@ public long getItemId(int position) { } @Override + @IonosCustomization public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { if (holder instanceof LocalFileListFooterViewHolder) { ((LocalFileListFooterViewHolder) holder).footerText.setText(getFooterText()); @@ -178,9 +180,7 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi gridViewHolder.checkbox.setVisibility(View.VISIBLE); if (isCheckedFile(file)) { gridViewHolder.itemLayout.setBackgroundColor(ContextCompat.getColor(mContext, R.color.selected_item_background)); - - gridViewHolder.checkbox.setImageDrawable( - viewThemeUtils.platform.tintDrawable(mContext, R.drawable.ic_checkbox_marked, ColorRole.PRIMARY)); + gridViewHolder.checkbox.setImageResource(R.drawable.ic_checkbox_marked); } else { gridViewHolder.itemLayout.setBackgroundColor(mContext.getResources().getColor(R.color.bg_default)); gridViewHolder.checkbox.setImageResource(R.drawable.ic_checkbox_blank_outline); diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java index 9c5dc23e862d..04ec0733fef1 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java @@ -28,6 +28,7 @@ import android.widget.LinearLayout; import com.elyeproj.loaderviewlibrary.LoaderImageView; +import com.ionos.annotation.IonosCustomization; import com.nextcloud.android.common.ui.theme.utils.ColorRole; import com.nextcloud.client.account.User; import com.nextcloud.client.jobs.upload.FileUploadHelper; @@ -113,8 +114,8 @@ public class OCFileListAdapter extends RecyclerView.Adapter { @@ -378,17 +372,6 @@ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int ); } } - case VIEWTYPE_IMAGE -> { - if (gridView) { - return new OCFileListGridImageViewHolder( - GridImageBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false) - ); - } else { - return new OCFileListItemViewHolder( - ListItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false) - ); - } - } case VIEWTYPE_FOOTER -> { return new OCFileListFooterViewHolder( ListFooterBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false) @@ -516,19 +499,9 @@ private void updateLivePhotoIndicators(ListGridImageViewHolder holder, OCFile fi } } + @IonosCustomization private void bindListGridItemViewHolder(ListGridItemViewHolder holder, OCFile file) { holder.getFileName().setText(file.getDecryptedFileName()); - - boolean gridImage = MimeTypeUtil.isImage(file) || MimeTypeUtil.isVideo(file); - if (gridView && gridImage) { - holder.getFileName().setVisibility(View.GONE); - } else { - if (gridView && ocFileListFragmentInterface.getColumnsCount() > showFilenameColumnThreshold) { - holder.getFileName().setVisibility(View.GONE); - } else { - holder.getFileName().setVisibility(View.VISIBLE); - } - } } private void bindListItemViewHolder(ListItemViewHolder holder, OCFile file) { @@ -705,7 +678,7 @@ public boolean shouldShowHeader() { return false; } - return !TextUtils.isEmpty(currentDirectory.getRichWorkspace().trim()); + return false; //Hide header in IONOS // !TextUtils.isEmpty(currentDirectory.getRichWorkspace().trim()); } /** diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt index 207c17ec533e..949b7c0e89a8 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListDelegate.kt @@ -16,6 +16,7 @@ import android.widget.ImageView import androidx.core.content.ContextCompat import androidx.core.content.res.ResourcesCompat import com.elyeproj.loaderviewlibrary.LoaderImageView +import com.ionos.annotation.IonosCustomization import com.nextcloud.android.common.ui.theme.utils.ColorRole import com.nextcloud.client.account.User import com.nextcloud.client.jobs.download.FileDownloadHelper @@ -194,6 +195,7 @@ class OCFileListDelegate( } } + @IonosCustomization fun bindGridViewHolder( gridViewHolder: ListGridImageViewHolder, file: OCFile, @@ -203,7 +205,7 @@ class OCFileListDelegate( // thumbnail gridViewHolder.imageFileName?.text = file.fileName gridViewHolder.thumbnail.tag = file.fileId - DisplayUtils.setThumbnail( + OCFileListThumbnailLoader( file, gridViewHolder.thumbnail, user, @@ -214,8 +216,9 @@ class OCFileListDelegate( gridViewHolder.shimmerThumbnail, preferences, viewThemeUtils, - syncFolderProvider - ) + syncFolderProvider, + gridViewHolder.fileIcon, + ).load() // item layout + click listeners bindGridItemLayout(file, gridViewHolder) @@ -265,8 +268,9 @@ class OCFileListDelegate( } } + @IonosCustomization private fun bindGridItemLayout(file: OCFile, gridViewHolder: ListGridImageViewHolder) { - setItemLayoutBackgroundColor(file, gridViewHolder) + setItemLayoutBackground(file, gridViewHolder) setCheckBoxImage(file, gridViewHolder) setItemLayoutOnClickListeners(file, gridViewHolder) @@ -290,6 +294,26 @@ class OCFileListDelegate( } } + @IonosCustomization + private fun setItemLayoutBackground(file: OCFile, gridViewHolder: ListGridImageViewHolder) { + val isSelected = file.fileId == highlightedItem?.fileId || isCheckedFile(file) + if (gridViewHolder is OCFileListGridItemViewHolder) { + val itemLayoutBackgroundResId = if (isSelected) { + R.drawable.grid_mode_selected_item_background + } else { + R.drawable.grid_mode_item_background + } + gridViewHolder.itemLayout.setBackgroundResource(itemLayoutBackgroundResId) + } else { + val itemLayoutBackgroundColor = if (isSelected) { + ContextCompat.getColor(context, R.color.selected_item_background) + } else { + ContextCompat.getColor(context, R.color.bg_default) + } + gridViewHolder.itemLayout.setBackgroundColor(itemLayoutBackgroundColor) + } + } + private fun setItemLayoutBackgroundColor(file: OCFile, gridViewHolder: ListGridImageViewHolder) { val cornerRadius = context.resources.getDimension(R.dimen.selected_grid_container_radius) @@ -313,11 +337,10 @@ class OCFileListDelegate( } } + @IonosCustomization private fun setCheckBoxImage(file: OCFile, gridViewHolder: ListGridImageViewHolder) { if (isCheckedFile(file)) { - gridViewHolder.checkbox.setImageDrawable( - viewThemeUtils.platform.tintDrawable(context, R.drawable.ic_checkbox_marked, ColorRole.PRIMARY) - ) + gridViewHolder.checkbox.setImageResource(R.drawable.ic_checkbox_marked) } else { gridViewHolder.checkbox.setImageResource(R.drawable.ic_checkbox_blank_outline) } @@ -365,6 +388,7 @@ class OCFileListDelegate( } } + @IonosCustomization private fun showShareIcon(gridViewHolder: ListGridImageViewHolder, file: OCFile) { val sharedIconView = gridViewHolder.shared if (gridViewHolder is OCFileListItemViewHolder || file.unreadCommentsCount == 0) { @@ -374,14 +398,18 @@ class OCFileListDelegate( sharedIconView.visibility = View.GONE } else { sharedIconView.visibility = View.VISIBLE - sharedIconView.setImageResource(R.drawable.shared_via_users) + sharedIconView.setImageResource(R.drawable.ic_filelist_shared_via_users) sharedIconView.contentDescription = context.getString(R.string.shared_icon_shared) } } else if (file.isSharedViaLink) { - sharedIconView.setImageResource(R.drawable.shared_via_link) + sharedIconView.setImageResource(R.drawable.ic_filelist_shared_via_link) sharedIconView.contentDescription = context.getString(R.string.shared_icon_shared_via_link) } else { - sharedIconView.setImageResource(R.drawable.ic_unshared) + if (gridViewHolder is OCFileListGridItemViewHolder) { + sharedIconView.setImageResource(R.drawable.ic_filelist_unshared_grid_mode) + } else { + sharedIconView.setImageResource(R.drawable.ic_filelist_unshared) + } sharedIconView.contentDescription = context.getString(R.string.shared_icon_share) } sharedIconView.setOnClickListener { ocFileListFragmentInterface.onShareIconClick(file) } diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridImageViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridImageViewHolder.kt index 776e6e9cee6f..1500c3acea69 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridImageViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridImageViewHolder.kt @@ -14,6 +14,7 @@ import android.widget.LinearLayout import android.widget.TextView import androidx.recyclerview.widget.RecyclerView import com.elyeproj.loaderviewlibrary.LoaderImageView +import com.ionos.annotation.IonosCustomization import com.owncloud.android.databinding.GridImageBinding internal class OCFileListGridImageViewHolder(var binding: GridImageBinding) : @@ -34,6 +35,9 @@ internal class OCFileListGridImageViewHolder(var binding: GridImageBinding) : override val shimmerThumbnail: LoaderImageView get() = binding.thumbnailShimmer + @IonosCustomization + override val fileIcon: ImageView + get() = binding.fileIcon override val favorite: ImageView get() = binding.favoriteAction override val localFileIndicator: ImageView diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridItemViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridItemViewHolder.kt index 05ee4731e994..3257c54fc277 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridItemViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListGridItemViewHolder.kt @@ -14,6 +14,7 @@ import android.widget.LinearLayout import android.widget.TextView import androidx.recyclerview.widget.RecyclerView import com.elyeproj.loaderviewlibrary.LoaderImageView +import com.ionos.annotation.IonosCustomization import com.owncloud.android.databinding.GridItemBinding internal class OCFileListGridItemViewHolder(var binding: GridItemBinding) : @@ -32,6 +33,9 @@ internal class OCFileListGridItemViewHolder(var binding: GridItemBinding) : override val shimmerThumbnail: LoaderImageView get() = binding.thumbnailShimmer + @IonosCustomization + override val fileIcon: ImageView + get() = binding.fileIcon override val favorite: ImageView get() = binding.favoriteAction override val localFileIndicator: ImageView diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListItemViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListItemViewHolder.kt index 38b3310f0bbe..ef7f80367603 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListItemViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListItemViewHolder.kt @@ -16,6 +16,7 @@ import androidx.recyclerview.widget.RecyclerView import com.elyeproj.loaderviewlibrary.LoaderImageView import com.google.android.material.chip.Chip import com.google.android.material.chip.ChipGroup +import com.ionos.annotation.IonosCustomization import com.owncloud.android.databinding.ListItemBinding import com.owncloud.android.ui.AvatarGroupLayout @@ -66,6 +67,9 @@ internal class OCFileListItemViewHolder(private var binding: ListItemBinding) : get() = null override val shimmerThumbnail: LoaderImageView get() = binding.thumbnailLayout.thumbnailShimmer + @IonosCustomization + override val fileIcon: ImageView + get() = binding.thumbnailLayout.fileIcon override val favorite: ImageView get() = binding.favoriteAction override val localFileIndicator: ImageView diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListThumbnailLoader.kt b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListThumbnailLoader.kt new file mode 100644 index 000000000000..8cfa3c03e40d --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/adapter/OCFileListThumbnailLoader.kt @@ -0,0 +1,147 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.owncloud.android.ui.adapter + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import android.os.AsyncTask +import android.view.View +import android.widget.ImageView +import com.elyeproj.loaderviewlibrary.LoaderImageView +import com.nextcloud.client.account.User +import com.nextcloud.client.preferences.AppPreferences +import com.owncloud.android.R +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.datamodel.SyncedFolderProvider +import com.owncloud.android.datamodel.ThumbnailsCacheManager +import com.owncloud.android.datamodel.ThumbnailsCacheManager.AsyncThumbnailDrawable +import com.owncloud.android.datamodel.ThumbnailsCacheManager.ThumbnailGenerationTask +import com.owncloud.android.datamodel.ThumbnailsCacheManager.ThumbnailGenerationTaskObject +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.utils.MimeTypeUtil +import com.owncloud.android.utils.theme.ViewThemeUtils + +class OCFileListThumbnailLoader( + private val file: OCFile, + private val thumbnailView: ImageView, + private val user: User, + private val storageManager: FileDataStorageManager, + private val asyncTasks: MutableList, + private val gridView: Boolean, + private val context: Context, + private val shimmerView: LoaderImageView, + private val preferences: AppPreferences, + private val viewThemeUtils: ViewThemeUtils, + private val syncedFolderProvider: SyncedFolderProvider?, + private val iconView: ImageView, +) { + + fun load() { + if (file.isFolder) { + showFolderIcon() + } else if (!file.isPreviewAvailable || file.remoteId == null) { + showFileIcon() + } else { + loadFromCacheOrRemote() + } + } + + private fun loadFromCacheOrRemote() { + val cacheKey = ThumbnailsCacheManager.PREFIX_THUMBNAIL + file.remoteId + val cachedThumbnail = ThumbnailsCacheManager.getBitmapFromDiskCache(cacheKey) + if (cachedThumbnail == null || file.isUpdateThumbnailNeeded) { + loadFromRemote() + } else if (MimeTypeUtil.isVideo(file)) { + val cachedThumbnailWithOverlay = ThumbnailsCacheManager.addVideoOverlay(cachedThumbnail, context) + showThumbnail(cachedThumbnailWithOverlay) + } else { + showThumbnail(cachedThumbnail) + } + } + + private fun loadFromRemote() { + if (!ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, thumbnailView)) { + return + } + + showShimmer() + + try { + val task = ThumbnailGenerationTask( + thumbnailView, + storageManager, + user, + asyncTasks, + gridView, + file.remoteId, + ) + + task.setListener(object : ThumbnailGenerationTask.Listener { + override fun onSuccess() { + showExistedThumbnail() + } + + override fun onError() { + showFileIcon() + } + }) + + val px = ThumbnailsCacheManager.getThumbnailDimension() + val tempBitmap = Bitmap.createBitmap(px, px, Bitmap.Config.RGB_565) + val asyncDrawable = AsyncThumbnailDrawable(context.resources, tempBitmap, task) + thumbnailView.setImageDrawable(asyncDrawable) + + asyncTasks.add(task) + val taskObject = ThumbnailGenerationTaskObject(file, file.remoteId) + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, taskObject) + } catch (e: Exception) { + Log_OC.d(this::class.simpleName, "ThumbnailGenerationTask : " + e.message) + } + } + + private fun showFolderIcon() { + val isAutoUploadFolder = SyncedFolderProvider.isAutoUploadFolder(syncedFolderProvider, file, user) + val isDarkModeActive = preferences.isDarkModeEnabled + val overlayIconId = file.getFileOverlayIconId(isAutoUploadFolder) + val fileIcon = MimeTypeUtil.getFileIcon(isDarkModeActive, overlayIconId, context, viewThemeUtils) + showIcon(fileIcon) + } + + private fun showFileIcon() { + val fileIcon = MimeTypeUtil.getFileTypeIcon(file.mimeType, file.fileName, context, viewThemeUtils) + showIcon(fileIcon) + } + + private fun showIcon(icon: Drawable) { + iconView.setImageDrawable(icon) + iconView.visibility = View.VISIBLE + thumbnailView.visibility = View.GONE + shimmerView.visibility = View.GONE + } + + private fun showThumbnail(thumbnail: Bitmap) { + thumbnailView.setImageBitmap(thumbnail) + showExistedThumbnail() + } + + private fun showExistedThumbnail() { + iconView.visibility = View.GONE + thumbnailView.visibility = View.VISIBLE + shimmerView.visibility = View.GONE + } + + private fun showShimmer() { + shimmerView.setImageResource(R.drawable.background) + shimmerView.resetLoader() + iconView.visibility = View.GONE + thumbnailView.visibility = View.GONE + shimmerView.visibility = View.VISIBLE + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/QuickSharingPermissionsAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/QuickSharingPermissionsAdapter.kt index 64ffa0350002..1041e5a018c9 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/QuickSharingPermissionsAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/QuickSharingPermissionsAdapter.kt @@ -14,6 +14,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView +import com.ionos.annotation.IonosCustomization import com.owncloud.android.databinding.ItemQuickSharePermissionsBinding import com.owncloud.android.datamodel.QuickPermissionModel import com.owncloud.android.utils.theme.ViewThemeUtils @@ -48,10 +49,10 @@ class QuickSharingPermissionsAdapter( RecyclerView .ViewHolder(itemView) { + @IonosCustomization("Disable icon tinting") fun bindData(quickPermissionModel: QuickPermissionModel) { binding.tvQuickShareName.text = quickPermissionModel.permissionName if (quickPermissionModel.isSelected) { - viewThemeUtils.platform.colorImageView(binding.tvQuickShareCheckIcon) binding.tvQuickShareCheckIcon.visibility = View.VISIBLE } else { binding.tvQuickShareCheckIcon.visibility = View.INVISIBLE diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.java b/app/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.java index 2e051086d91b..be528d1e149f 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.java +++ b/app/src/main/java/com/owncloud/android/ui/adapter/ShareeListAdapter.java @@ -17,6 +17,7 @@ import android.view.ViewGroup; import android.widget.ImageView; +import com.ionos.annotation.IonosCustomization; import com.nextcloud.client.account.User; import com.owncloud.android.R; import com.owncloud.android.databinding.FileDetailsShareInternalShareLinkBinding; @@ -183,6 +184,7 @@ public void remove(OCShare share) { /** * sort all by creation time, then email/link shares on top */ + @IonosCustomization("Hide internal share link") protected final void sortShares() { List links = new ArrayList<>(); List users = new ArrayList<>(); @@ -201,12 +203,6 @@ protected final void sortShares() { shares = links; shares.addAll(users); - // add internal share link at end - if (!encrypted) { - final OCShare ocShare = new OCShare(); - ocShare.setShareType(ShareType.INTERNAL); - shares.add(ocShare); - } } public List getShares() { diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.kt b/app/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.kt index 215f23f1f4b3..17c1f77b3ce4 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.kt @@ -9,15 +9,20 @@ package com.owncloud.android.ui.adapter import android.annotation.SuppressLint import android.content.Context +import android.graphics.drawable.Drawable import android.view.LayoutInflater import android.view.MenuItem import android.view.View import android.view.ViewGroup import android.widget.ImageButton import android.widget.PopupMenu +import androidx.annotation.ColorRes +import androidx.annotation.DrawableRes import androidx.annotation.VisibleForTesting +import androidx.appcompat.content.res.AppCompatResources import com.afollestad.sectionedrecyclerview.SectionedRecyclerViewAdapter import com.afollestad.sectionedrecyclerview.SectionedViewHolder +import com.ionos.annotation.IonosCustomization import com.nextcloud.android.common.ui.theme.utils.ColorRole import com.nextcloud.client.core.Clock import com.owncloud.android.R @@ -240,6 +245,7 @@ class SyncedFolderAdapter( return -1 } + @IonosCustomization override fun onBindHeaderViewHolder(commonHolder: SectionedViewHolder, section: Int, expanded: Boolean) { if (section < filteredSyncFolderItems.size) { val holder = commonHolder as HeaderViewHolder @@ -248,11 +254,11 @@ class SyncedFolderAdapter( holder.binding.title.text = filteredSyncFolderItems[section].folderName if (MediaFolderType.VIDEO == filteredSyncFolderItems[section].type) { - holder.binding.type.setImageResource(R.drawable.video_32dp) + holder.binding.type.setImageDrawable(colorDrawable(R.drawable.video_32dp)) } else if (MediaFolderType.IMAGE == filteredSyncFolderItems[section].type) { - holder.binding.type.setImageResource(R.drawable.image_32dp) + holder.binding.type.setImageDrawable(colorDrawable(R.drawable.image_32dp)) } else { - holder.binding.type.setImageResource(R.drawable.folder_star_32dp) + holder.binding.type.setImageDrawable(colorDrawable(R.drawable.folder_star_32dp)) } holder.binding.syncStatusButton.visibility = View.VISIBLE @@ -437,16 +443,25 @@ class SyncedFolderAdapter( binding.root ) + @IonosCustomization private fun setSyncButtonActiveIcon(syncStatusButton: ImageButton, enabled: Boolean) { if (enabled) { - syncStatusButton.setImageDrawable( - viewThemeUtils.platform.tintDrawable(context, R.drawable.ic_cloud_sync_on, ColorRole.PRIMARY) - ) + syncStatusButton.setImageDrawable(colorDrawable(R.drawable.ic_cloud_sync_on)) } else { - syncStatusButton.setImageResource(R.drawable.ic_cloud_sync_off) + syncStatusButton.setImageDrawable(colorDrawable(R.drawable.ic_cloud_sync_off)) } } + @IonosCustomization + private fun colorDrawable( + @DrawableRes drawableRes: Int, + @ColorRes colorRes: Int = R.color.default_icon_color, + context: Context = this.context, + ): Drawable? = + AppCompatResources.getDrawable(context, drawableRes)?.let { drawable -> + viewThemeUtils.platform.colorDrawable(drawable, context.getColor(colorRes)) + } + companion object { private const val VIEW_TYPE_EMPTY = Int.MAX_VALUE private const val VIEW_TYPE_ITEM = 1 diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt index 69054c3ed240..0d73367c6581 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/UnifiedSearchItemViewHolder.kt @@ -11,18 +11,20 @@ import android.content.Context import android.graphics.Bitmap import android.graphics.drawable.Drawable import android.view.View +import androidx.core.content.ContextCompat import androidx.core.content.res.ResourcesCompat import com.afollestad.sectionedrecyclerview.SectionedViewHolder import com.bumptech.glide.Glide import com.bumptech.glide.request.RequestListener import com.bumptech.glide.request.target.Target -import com.nextcloud.android.common.ui.theme.utils.ColorRole +import com.ionos.annotation.IonosCustomization import com.nextcloud.client.account.User import com.nextcloud.client.network.ClientFactory import com.nextcloud.model.SearchResultEntryType import com.nextcloud.utils.CalendarEventManager import com.nextcloud.utils.ContactManager import com.nextcloud.utils.extensions.getType +import com.owncloud.android.R import com.owncloud.android.databinding.UnifiedSearchItemBinding import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.lib.common.SearchResultEntry @@ -110,6 +112,7 @@ class UnifiedSearchItemViewHolder( } } + @IonosCustomization private fun getPlaceholder( entry: SearchResultEntry, entryType: SearchResultEntryType, @@ -121,7 +124,8 @@ class UnifiedSearchItemViewHolder( val defaultDrawable = MimeTypeUtil.getFileTypeIcon(mimetype, entry.title, context, viewThemeUtils) val drawable: Drawable = ResourcesCompat.getDrawable(context.resources, iconId, null) ?: defaultDrawable - return viewThemeUtils.platform.tintDrawable(context, drawable, ColorRole.PRIMARY) + val color = ContextCompat.getColor(context, R.color.filelist_file_icon_color) + return viewThemeUtils.platform.colorDrawable(drawable, color) } private inner class RoundIfNeededListener(private val entry: SearchResultEntry) : diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/LoadingDialog.kt b/app/src/main/java/com/owncloud/android/ui/dialog/LoadingDialog.kt index ed179977f7af..c441304a9b62 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/LoadingDialog.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/LoadingDialog.kt @@ -8,13 +8,13 @@ */ package com.owncloud.android.ui.dialog +import android.app.Dialog import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup import androidx.fragment.app.DialogFragment -import com.nextcloud.android.common.ui.theme.utils.ColorRole +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.ionos.annotation.IonosCustomization import com.nextcloud.client.di.Injectable +import com.owncloud.android.R import com.owncloud.android.databinding.LoadingDialogBinding import com.owncloud.android.utils.theme.ViewThemeUtils import javax.inject.Inject @@ -35,18 +35,13 @@ class LoadingDialog : DialogFragment(), Injectable { isCancelable = false } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { - binding = LoadingDialogBinding.inflate(inflater, container, false) + @IonosCustomization + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + binding = LoadingDialogBinding.inflate(layoutInflater) binding.loadingText.text = mMessage - - val loadingDrawable = binding.loadingBar.indeterminateDrawable - if (loadingDrawable != null) { - viewThemeUtils?.platform?.tintDrawable(requireContext(), loadingDrawable) - } - - viewThemeUtils?.platform?.colorViewBackground(binding.loadingLayout, ColorRole.SURFACE) - - return binding.root + return MaterialAlertDialogBuilder(requireContext(), R.style.Theme_ownCloud_LoadingDialog) + .setView(binding.root) + .create() } override fun onDestroyView() { diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/SendFilesDialog.kt b/app/src/main/java/com/owncloud/android/ui/dialog/SendFilesDialog.kt index 4f851b3d99e5..87faeeb7161e 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/SendFilesDialog.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/SendFilesDialog.kt @@ -56,7 +56,7 @@ class SendFilesDialog : BottomSheetDialogFragment(R.layout.send_files_fragment), binding = SendFilesFragmentBinding.inflate(inflater, container, false) setupSendButtonRecyclerView() - viewThemeUtils?.platform?.colorViewBackground(binding.bottomSheet, ColorRole.SURFACE) + viewThemeUtils?.ionos?.platform?.colorViewBackground(binding.bottomSheet, ColorRole.SURFACE) return binding.root } diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.kt b/app/src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.kt index 9be56aff7ec6..084109ab1818 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/SendShareDialog.kt @@ -102,7 +102,7 @@ class SendShareDialog : BottomSheetDialogFragment(R.layout.send_share_fragment), private fun applyTintColor() { viewThemeUtils?.material?.colorMaterialButtonPrimaryFilled(binding.btnLink) viewThemeUtils?.material?.colorMaterialButtonPrimaryFilled(binding.btnShare) - viewThemeUtils?.platform?.colorViewBackground(binding.bottomSheet, ColorRole.SURFACE) + viewThemeUtils?.ionos?.platform?.colorViewBackground(binding.bottomSheet, ColorRole.SURFACE) } @Suppress("MagicNumber") diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/SortingOrderDialogFragment.kt b/app/src/main/java/com/owncloud/android/ui/dialog/SortingOrderDialogFragment.kt index 8a593cabf541..5d12b5695c79 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/SortingOrderDialogFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/SortingOrderDialogFragment.kt @@ -14,6 +14,7 @@ import android.os.Bundle import android.view.View import androidx.fragment.app.DialogFragment import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.ionos.annotation.IonosCustomization import com.nextcloud.client.di.Injectable import com.owncloud.android.R import com.owncloud.android.databinding.SortingOrderFragmentBinding @@ -49,6 +50,7 @@ class SortingOrderDialogFragment : DialogFragment(), Injectable { * * @param binding the parent binding */ + @IonosCustomization("Remove material buttons styling") private fun setupDialogElements(binding: SortingOrderFragmentBinding) { val bindings = listOf( binding.sortByNameAscending to FileSortOrder.SORT_A_TO_Z, @@ -63,11 +65,9 @@ class SortingOrderDialogFragment : DialogFragment(), Injectable { view.tag = sortOrder view.let { it.setOnClickListener(OnSortOrderClickListener()) - viewThemeUtils?.material?.colorMaterialButtonPrimaryBorderless(it) } } - viewThemeUtils?.material?.colorMaterialButtonPrimaryTonal(binding.cancel) binding.cancel.setOnClickListener { dismiss() } } diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/StoragePermissionDialogFragment.kt b/app/src/main/java/com/owncloud/android/ui/dialog/StoragePermissionDialogFragment.kt index 220f28edd680..82fc80031827 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/StoragePermissionDialogFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/StoragePermissionDialogFragment.kt @@ -17,6 +17,7 @@ import androidx.core.os.bundleOf import androidx.fragment.app.DialogFragment import com.google.android.material.button.MaterialButton import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.ionos.annotation.IonosCustomization import com.nextcloud.client.di.Injectable import com.owncloud.android.R import com.owncloud.android.utils.theme.ViewThemeUtils @@ -59,29 +60,26 @@ class StoragePermissionDialogFragment : DialogFragment(), Injectable { } } + @IonosCustomization override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val title = when { - permissionRequired -> R.string.file_management_permission - else -> R.string.file_management_permission_optional + permissionRequired -> R.string.ionos_file_management_permission + else -> R.string.ionos_file_management_permission_optional } val explanationResource = when { - permissionRequired -> R.string.file_management_permission_text - else -> R.string.file_management_permission_optional_text + permissionRequired -> R.string.ionos_file_management_permission_text + else -> R.string.ionos_file_management_permission_optional_text } val message = getString(explanationResource, getString(R.string.app_name)) - val dialogBuilder = MaterialAlertDialogBuilder(requireContext()) + val dialogBuilder = MaterialAlertDialogBuilder(requireContext(), R.style.Theme_ownCloud_Dialog) .setTitle(title) .setMessage(message) - .setPositiveButton(R.string.storage_permission_full_access) { _, _ -> + .setPositiveButton(R.string.permission_allow) { _, _ -> setResult(Result.FULL_ACCESS) dismiss() } - .setNegativeButton(R.string.storage_permission_media_read_only) { _, _ -> - setResult(Result.MEDIA_READ_ONLY) - dismiss() - } - .setNeutralButton(R.string.common_cancel) { _, _ -> + .setNegativeButton(R.string.permission_deny) { _, _ -> setResult(Result.CANCEL) dismiss() } diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/setupEncryption/SetupEncryptionDialogFragment.kt b/app/src/main/java/com/owncloud/android/ui/dialog/setupEncryption/SetupEncryptionDialogFragment.kt index becfda347fa9..132b5d00f7c1 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/setupEncryption/SetupEncryptionDialogFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/setupEncryption/SetupEncryptionDialogFragment.kt @@ -20,6 +20,7 @@ import androidx.appcompat.app.AlertDialog import androidx.fragment.app.DialogFragment import com.google.android.material.button.MaterialButton import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.ionos.annotation.IonosCustomization import com.nextcloud.client.account.User import com.nextcloud.client.di.Injectable import com.nextcloud.client.network.ClientFactory @@ -494,6 +495,7 @@ class SetupEncryptionDialogFragment : DialogFragment(), Injectable { } @VisibleForTesting + @IonosCustomization fun showMnemonicInfo() { if (dialog == null) { Log_OC.e(TAG, "Dialog is null cannot proceed further.") @@ -511,7 +513,7 @@ class SetupEncryptionDialogFragment : DialogFragment(), Injectable { positiveButton?.let { positiveButton -> negativeButton?.let { negativeButton -> - viewThemeUtils.platform.colorTextButtons(positiveButton, negativeButton) + viewThemeUtils.ionos.platform.colorTextButtons(positiveButton, negativeButton) } } @@ -519,6 +521,7 @@ class SetupEncryptionDialogFragment : DialogFragment(), Injectable { } @VisibleForTesting + @IonosCustomization fun errorSavingKeys() { if (dialog == null) { Log_OC.e(TAG, "Dialog is null cannot proceed further.") @@ -533,7 +536,7 @@ class SetupEncryptionDialogFragment : DialogFragment(), Injectable { positiveButton?.setText(R.string.end_to_end_encryption_dialog_close) positiveButton?.visibility = View.VISIBLE positiveButton?.let { - viewThemeUtils.platform.colorTextButtons(it) + viewThemeUtils.ionos.platform.colorTextButtons(it) } } diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.java index 90d5ff624628..f408848d6759 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.java @@ -41,6 +41,7 @@ import android.widget.TextView; import com.google.android.material.button.MaterialButton; +import com.ionos.annotation.IonosCustomization; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.di.Injectable; import com.nextcloud.client.preferences.AppPreferences; @@ -105,7 +106,6 @@ public class ExtendedListFragment extends Fragment implements @Inject UserAccountManager accountManager; @Inject ViewThemeUtils viewThemeUtils; - private ScaleGestureDetector mScaleGestureDetector; protected SwipeRefreshLayout mRefreshListLayout; protected MaterialButton mSortButton; protected MaterialButton mSwitchGridViewButton; @@ -169,10 +169,10 @@ public boolean isGridEnabled() { } @Override + @IonosCustomization public void onCreateOptionsMenu(Menu menu, @NonNull MenuInflater inflater) { final MenuItem item = menu.findItem(R.id.action_search); searchView = (SearchView) MenuItemCompat.getActionView(item); - viewThemeUtils.androidx.themeToolbarSearchView(searchView); closeButton = searchView.findViewById(androidx.appcompat.R.id.search_close_btn); searchView.setOnQueryTextListener(this); searchView.setOnCloseListener(this); @@ -317,6 +317,7 @@ public void onAttach(@NonNull Context context) { } @Override + @IonosCustomization public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Log_OC.d(TAG, "onCreateView"); @@ -334,18 +335,6 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, mScale = preferences.getGridColumns(); setGridViewColumns(1f); - mScaleGestureDetector = new ScaleGestureDetector(MainApp.getAppContext(), new ScaleListener()); - - getRecyclerView().setOnTouchListener((view, motionEvent) -> { - mScaleGestureDetector.onTouchEvent(motionEvent); - - if (motionEvent.getAction() == MotionEvent.ACTION_UP) { - view.performClick(); - } - - return false; - }); - // Pull-down to refresh layout mRefreshListLayout = binding.swipeContainingList; viewThemeUtils.androidx.themeSwipeRefreshLayout(mRefreshListLayout); @@ -602,6 +591,7 @@ public void run() { }); } + @IonosCustomization public void setEmptyListMessage(final SearchType searchType) { new Handler(Looper.getMainLooper()).post(new Runnable() { @Override @@ -610,16 +600,15 @@ public void run() { if (searchType == SearchType.NO_SEARCH) { setMessageForEmptyList(R.string.file_list_empty_headline, R.string.file_list_empty, - R.drawable.ic_list_empty_folder, - true); + R.drawable.ic_list_empty_folder); } else if (searchType == SearchType.FILE_SEARCH) { setMessageForEmptyList(R.string.file_list_empty_headline_server_search, R.string.file_list_empty, - R.drawable.ic_search_light_grey); + R.drawable.ic_search); } else if (searchType == SearchType.FAVORITE_SEARCH) { setMessageForEmptyList(R.string.file_list_empty_favorite_headline, R.string.file_list_empty_favorites_filter_list, - R.drawable.ic_star_light_yellow); + R.drawable.favorite); } else if (searchType == SearchType.RECENTLY_MODIFIED_SEARCH) { setMessageForEmptyList(R.string.file_list_empty_headline_server_search, R.string.file_list_empty_recently_modified, @@ -627,7 +616,7 @@ public void run() { } else if (searchType == SearchType.REGULAR_FILTER) { setMessageForEmptyList(R.string.file_list_empty_headline_search, R.string.file_list_empty_search, - R.drawable.ic_search_light_grey); + R.drawable.ic_search); } else if (searchType == SearchType.SHARED_FILTER) { setMessageForEmptyList(R.string.file_list_empty_shared_headline, R.string.file_list_empty_shared, @@ -639,7 +628,7 @@ public void run() { } else if (searchType == SearchType.LOCAL_SEARCH) { setMessageForEmptyList(R.string.file_list_empty_headline_server_search, R.string.file_list_empty_local_search, - R.drawable.ic_search_light_grey); + R.drawable.ic_search); } } }); @@ -680,18 +669,11 @@ public void onRefresh(boolean ignoreETag) { } @Override + @IonosCustomization public void onConfigurationChanged(@NonNull Configuration newConfig) { super.onConfigurationChanged(newConfig); - - if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) { - maxColumnSize = 10; - } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { - maxColumnSize = 5; - } - - if (isGridEnabled() && getColumnsCount() > maxColumnSize) { - ((GridLayoutManager) getRecyclerView().getLayoutManager()).setSpanCount(maxColumnSize); - } + mScale = preferences.getGridColumns(); + setGridViewColumns(1f); } protected void setGridSwitchButton() { diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java index b02a85ae5022..c75848c98ccf 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -22,7 +22,7 @@ import com.google.android.material.chip.Chip; import com.google.android.material.floatingactionbutton.FloatingActionButton; -import com.google.android.material.tabs.TabLayout; +import com.ionos.annotation.IonosCustomization; import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.di.Injectable; @@ -76,7 +76,6 @@ import androidx.annotation.Nullable; import androidx.core.content.res.ResourcesCompat; import androidx.fragment.app.FragmentManager; -import androidx.viewpager2.widget.ViewPager2; /** * This Fragment is used to display the details about a file. @@ -296,66 +295,15 @@ private void onOverflowIconClicked() { .show(fragmentManager, "actions"); } + @IonosCustomization("Hide tabs in IONOS") private void setupViewPager() { - binding.tabLayout.removeAllTabs(); - - binding.tabLayout.addTab(binding.tabLayout.newTab().setText(R.string.drawer_item_activities).setIcon(R.drawable.ic_activity)); - - - if (showSharingTab()) { - binding.tabLayout.addTab(binding.tabLayout.newTab().setText(R.string.share_dialog_title).setIcon(R.drawable.shared_via_users)); - } - - if (MimeTypeUtil.isImage(getFile())) { - binding.tabLayout.addTab(binding.tabLayout.newTab().setText(R.string.filedetails_details).setIcon(R.drawable.image_32dp)); - } - - viewThemeUtils.material.themeTabLayout(binding.tabLayout); + binding.tabLayout.setVisibility(View.GONE); final FileDetailTabAdapter adapter = new FileDetailTabAdapter(requireActivity(), getFile(), user, showSharingTab()); binding.pager.setAdapter(adapter); - - binding.pager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - final FileDetailActivitiesFragment fragment = getFileDetailActivitiesFragment(); - if (activeTab == 0 && fragment != null) { - fragment.markCommentsAsRead(); - } - super.onPageScrolled(position, positionOffset, positionOffsetPixels); - } - }); - binding.tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { - @Override - public void onTabSelected(TabLayout.Tab tab) { - binding.pager.setCurrentItem(tab.getPosition()); - if (tab.getPosition() == 0) { - final FileDetailActivitiesFragment fragment = getFileDetailActivitiesFragment(); - if (fragment != null) { - fragment.markCommentsAsRead(); - } - } - } - - @Override - public void onTabUnselected(TabLayout.Tab tab) { - // unused at the moment - } - - @Override - public void onTabReselected(TabLayout.Tab tab) { - // unused at the moment - } - }); - - binding.tabLayout.post(() -> { - TabLayout.Tab tab1 = binding.tabLayout.getTabAt(activeTab); - if (tab1 == null) return; - tab1.select(); - }); } @Override @@ -783,8 +731,8 @@ public void initiateSharingProcess(String shareeName, * * @param isFragmentReplaced */ + @IonosCustomization("Hide tabs in IONOS") public void showHideFragmentView(boolean isFragmentReplaced) { - binding.tabLayout.setVisibility(isFragmentReplaced ? View.GONE : View.VISIBLE); binding.pager.setVisibility(isFragmentReplaced ? View.GONE : View.VISIBLE); binding.sharingFrameContainer.setVisibility(isFragmentReplaced ? View.VISIBLE : View.GONE); FloatingActionButton mFabMain = requireActivity().findViewById(R.id.fab_main); diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index 11094ad20d02..c2b84bed85be 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -31,6 +31,7 @@ import android.view.View; import android.view.ViewGroup; +import com.ionos.annotation.IonosCustomization; import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.di.Injectable; @@ -209,6 +210,7 @@ public void onStop() { searchConfig.reset(); } + @IonosCustomization private void setupView() { setShareWithYou(); @@ -218,7 +220,6 @@ private void setupView() { (SearchManager) fileActivity.getSystemService(Context.SEARCH_SERVICE), binding.searchView, fileActivity.getComponentName()); - viewThemeUtils.androidx.themeToolbarSearchView(binding.searchView); if (file.canReshare()) { @@ -236,7 +237,7 @@ private void setupView() { } } } else { - binding.searchView.setQueryHint(getResources().getString(R.string.share_search)); + binding.searchView.setQueryHint(getResources().getString(R.string.ionos_share_search)); } } else { binding.searchView.setQueryHint(getResources().getString(R.string.resharing_is_not_allowed)); diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingMenuBottomSheetDialog.java b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingMenuBottomSheetDialog.java index bf2c1ae72d1c..a49b4ae0762a 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingMenuBottomSheetDialog.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingMenuBottomSheetDialog.java @@ -12,10 +12,10 @@ import android.os.Bundle; import android.view.View; -import android.view.ViewGroup; import com.google.android.material.bottomsheet.BottomSheetBehavior; import com.google.android.material.bottomsheet.BottomSheetDialog; +import com.ionos.annotation.IonosCustomization; import com.owncloud.android.databinding.FileDetailsSharingMenuBottomSheetFragmentBinding; import com.owncloud.android.lib.resources.shares.OCShare; import com.owncloud.android.lib.resources.shares.ShareType; @@ -42,22 +42,13 @@ public FileDetailSharingMenuBottomSheetDialog(FileActivity fileActivity, } @Override + @IonosCustomization("Remove custom window LayoutParams. Disable icon tinting") protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = FileDetailsSharingMenuBottomSheetFragmentBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - if (getWindow() != null) { - getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); - } - - viewThemeUtils.platform.themeDialog(binding.getRoot()); - - viewThemeUtils.platform.colorImageView(binding.menuIconAddAnotherLink); - viewThemeUtils.platform.colorImageView(binding.menuIconAdvancedPermissions); - viewThemeUtils.platform.colorImageView(binding.menuIconSendLink); - viewThemeUtils.platform.colorImageView(binding.menuIconUnshare); - viewThemeUtils.platform.colorImageView(binding.menuIconSendNewEmail); + viewThemeUtils.ionos.platform.themeDialog(binding.getRoot()); updateUI(); diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.java index b264078da243..9cdd44ec3250 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragment.java @@ -22,6 +22,7 @@ import android.view.View; import android.view.ViewGroup; +import com.ionos.annotation.IonosCustomization; import com.nextcloud.utils.extensions.IntentExtensionsKt; import com.owncloud.android.BuildConfig; import com.owncloud.android.R; @@ -35,7 +36,6 @@ import com.owncloud.android.ui.activity.ToolbarActivity; import com.owncloud.android.ui.adapter.CommonOCFileListAdapterInterface; import com.owncloud.android.ui.adapter.GalleryAdapter; -import com.owncloud.android.ui.adapter.OCFileListDelegate; import com.owncloud.android.ui.asynctasks.GallerySearchTask; import com.owncloud.android.ui.events.ChangeMenuEvent; @@ -68,7 +68,8 @@ public class GalleryFragment extends OCFileListFragment implements GalleryFragme @Inject FileDataStorageManager fileDataStorageManager; private final static int maxColumnSizeLandscape = 5; - private final static int maxColumnSizePortrait = 2; + @IonosCustomization("increased quantity") + private final static int maxColumnSizePortrait = 3; private int columnSize; protected void setPhotoSearchQueryRunning(boolean value) { diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragmentBottomSheetDialog.kt b/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragmentBottomSheetDialog.kt index 178c5e6bb3c3..9d54fc804a1e 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragmentBottomSheetDialog.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/GalleryFragmentBottomSheetDialog.kt @@ -13,7 +13,10 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetDialog import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import com.ionos.annotation.IonosCustomization import com.nextcloud.android.common.ui.theme.utils.ColorRole import com.nextcloud.client.di.Injectable import com.owncloud.android.R @@ -32,6 +35,9 @@ class GalleryFragmentBottomSheetDialog( override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { binding = FragmentGalleryBottomSheetBinding.inflate(layoutInflater, container, false) + val bottomSheetDialog = dialog as BottomSheetDialog + bottomSheetDialog.behavior.state = BottomSheetBehavior.STATE_EXPANDED + bottomSheetDialog.behavior.skipCollapsed = true return binding.root } @@ -41,23 +47,9 @@ class GalleryFragmentBottomSheetDialog( setupClickListener() } + @IonosCustomization private fun setupLayout() { - viewThemeUtils.platform.colorViewBackground(binding.bottomSheet, ColorRole.SURFACE) - - listOf( - binding.tickMarkShowImages, - binding.tickMarkShowVideos - ).forEach { - viewThemeUtils.platform.colorImageView(it, ColorRole.PRIMARY) - } - - listOf( - binding.btnSelectMediaFolder, - binding.btnHideVideos, - binding.btnHideImages - ).forEach { - viewThemeUtils.material.colorMaterialButtonText(it) - } + viewThemeUtils.ionos.platform.colorViewBackground(binding.bottomSheet, ColorRole.SURFACE) when (currentMediaState) { MediaState.MEDIA_STATE_PHOTOS_ONLY -> { diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/LocalFileListFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/LocalFileListFragment.java index e82f04e04129..1fccb802dc48 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/LocalFileListFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/LocalFileListFragment.java @@ -20,6 +20,7 @@ import android.view.View; import android.view.ViewGroup; +import com.ionos.annotation.IonosCustomization; import com.nextcloud.client.di.Injectable; import com.nextcloud.client.preferences.AppPreferences; import com.owncloud.android.R; @@ -91,16 +92,17 @@ public void onAttach(@NonNull Activity activity) { * {@inheritDoc} */ @Override + @IonosCustomization public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Log_OC.i(TAG, "onCreateView() start"); View v = super.onCreateView(inflater, container, savedInstanceState); if (!mContainerActivity.isFolderPickerMode()) { setMessageForEmptyList(R.string.file_list_empty_headline, R.string.local_file_list_empty, - R.drawable.ic_list_empty_folder, true); + R.drawable.ic_list_empty_folder); } else { setMessageForEmptyList(R.string.folder_list_empty_headline, R.string.local_folder_list_empty, - R.drawable.ic_list_empty_folder, true); + R.drawable.ic_list_empty_folder); } setSwipeEnabled(false); // Disable pull-to-refresh @@ -114,6 +116,7 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, * {@inheritDoc} */ @Override + @IonosCustomization public void onActivityCreated(Bundle savedInstanceState) { Log_OC.i(TAG, "onActivityCreated() start"); @@ -136,7 +139,7 @@ public void onActivityCreated(Bundle savedInstanceState) { }); FileSortOrder sortOrder = preferences.getSortOrderByType(FileSortOrder.Type.localFileListView); - mSortButton.setText(DisplayUtils.getSortOrderStringId(sortOrder)); + mSortButton.setIconResource(DisplayUtils.getSortOrderIconRes(sortOrder)); setGridSwitchButton(); mSwitchGridViewButton.setOnClickListener(v -> { @@ -303,8 +306,9 @@ public int getFilesCount() { return mAdapter.getFilesCount(); } + @IonosCustomization public void sortFiles(FileSortOrder sortOrder) { - mSortButton.setText(DisplayUtils.getSortOrderStringId(sortOrder)); + mSortButton.setIconResource(DisplayUtils.getSortOrderIconRes(sortOrder)); mAdapter.setSortOrder(sortOrder); } diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java index d6725c51d7ef..51d3bcaab0c5 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java @@ -40,6 +40,8 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.google.android.material.snackbar.Snackbar; +import com.ionos.annotation.IonosCustomization; +import com.ionos.scanbot.controller.ScanbotController; import com.nextcloud.android.lib.resources.files.ToggleFileLockRemoteOperation; import com.nextcloud.android.lib.richWorkspace.RichWorkspaceDirectEditingRemoteOperation; import com.nextcloud.client.account.User; @@ -47,7 +49,6 @@ import com.nextcloud.client.device.DeviceInfo; import com.nextcloud.client.di.Injectable; import com.nextcloud.client.documentscan.AppScanOptionalFeature; -import com.nextcloud.client.documentscan.DocumentScanActivity; import com.nextcloud.client.editimage.EditImageActivity; import com.nextcloud.client.jobs.BackgroundJobManager; import com.nextcloud.client.network.ClientFactory; @@ -211,6 +212,7 @@ public class OCFileListFragment extends ExtendedListFragment implements @Inject ShortcutUtil shortcutUtil; @Inject SyncedFolderProvider syncedFolderProvider; @Inject AppScanOptionalFeature appScanOptionalFeature; + @Inject ScanbotController scanbotController; protected FileFragment.ContainerActivity mContainerActivity; @@ -307,6 +309,7 @@ public void onAttach(@NonNull Context context) { * {@inheritDoc} */ @Override + @IonosCustomization public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Log_OC.i(TAG, "onCreateView() start"); View v = super.onCreateView(inflater, container, savedInstanceState); @@ -330,11 +333,6 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, mFabMain = requireActivity().findViewById(R.id.fab_main); - if (mFabMain != null) { - // is not available in FolderPickerActivity - viewThemeUtils.material.themeFAB(mFabMain); - } - Log_OC.i(TAG, "onCreateView() end"); return v; } @@ -479,22 +477,19 @@ protected void prepareCurrentSearch(SearchEvent event) { /** * register listener on FAB. */ + @IonosCustomization("Show simple FAB menu bottom sheet") public void registerFabListener() { FileActivity activity = (FileActivity) getActivity(); if (mFabMain != null) { // is not available in FolderPickerActivity - viewThemeUtils.material.themeFAB(mFabMain); mFabMain.setOnClickListener(v -> { - final OCFileListBottomSheetDialog dialog = - new OCFileListBottomSheetDialog(activity, + final SimpleOCFileListBottomSheetDialog dialog = + new SimpleOCFileListBottomSheetDialog(activity, this, deviceInfo, - accountManager.getUser(), - getCurrentFile(), themeUtils, viewThemeUtils, - editorUtils, appScanOptionalFeature); dialog.getBehavior().setState(BottomSheetBehavior.STATE_EXPANDED); @@ -559,9 +554,7 @@ public void scanDocUpload() { final OCFile currentFile = getCurrentFile(); if (fileDisplayActivity != null && currentFile != null && currentFile.isFolder()) { - Intent intent = new Intent(requireContext(), DocumentScanActivity.class); - intent.putExtra(DocumentScanActivity.EXTRA_FOLDER, currentFile.getRemotePath()); - startActivity(intent); + scanbotController.scanToDocument(requireContext(), currentFile.getRemotePath()); } else { Log.w(TAG, "scanDocUpload: Failed to start doc scanning, fileDisplayActivity=" + fileDisplayActivity + ", currentFile=" + currentFile); @@ -760,6 +753,7 @@ public void onItemCheckedStateChanged(ActionMode mode, int position, long id, bo * Load menu and customize UI when action mode is started. */ @Override + @IonosCustomization public boolean onCreateActionMode(ActionMode mode, Menu menu) { mActiveActionMode = mode; // Determine if actionMode is "new" or not (already affected by item-selection) @@ -768,8 +762,6 @@ public boolean onCreateActionMode(ActionMode mode, Menu menu) { // fake menu to be able to use bottom sheet instead MenuInflater inflater = getActivity().getMenuInflater(); inflater.inflate(R.menu.custom_menu_placeholder, menu); - final MenuItem item = menu.findItem(R.id.custom_menu_placeholder_item); - item.setIcon(viewThemeUtils.platform.colorDrawable(item.getIcon(), ContextCompat.getColor(requireContext(), R.color.white))); mode.invalidate(); //set actionMode color @@ -829,7 +821,7 @@ public void onDestroyActionMode(ActionMode mode) { Activity activity = getActivity(); if (activity != null) { - viewThemeUtils.platform.resetStatusBar(activity); + viewThemeUtils.ionos.platform.resetSystemBars(activity); } getCommonAdapter().setMultiSelect(false); @@ -1455,6 +1447,7 @@ public void updateOCFile(@NonNull OCFile file) { mAdapter.notifyItemChanged(file); } + @IonosCustomization private void updateLayout() { // decide grid vs list view if (isGridViewPreferred(mFile)) { @@ -1464,7 +1457,7 @@ private void updateLayout() { } if (mSortButton != null) { - mSortButton.setText(DisplayUtils.getSortOrderStringId(preferences.getSortOrderByFolder(mFile))); + mSortButton.setIconResource(DisplayUtils.getSortOrderIconRes(preferences.getSortOrderByFolder(mFile))); } if (mSwitchGridViewButton != null) { setGridSwitchButton(); @@ -1489,8 +1482,9 @@ private void invalidateActionMode() { } } + @IonosCustomization public void sortFiles(FileSortOrder sortOrder) { - mSortButton.setText(DisplayUtils.getSortOrderStringId(sortOrder)); + mSortButton.setIconResource(DisplayUtils.getSortOrderIconRes(sortOrder)); mAdapter.setSortOrder(mFile, sortOrder); } @@ -2049,6 +2043,7 @@ public boolean isLoading() { * * @param visible Desired visibility for the FAB. */ + @IonosCustomization public void setFabVisible(final boolean visible) { if (mFabMain == null) { // is not available in FolderPickerActivity @@ -2059,7 +2054,6 @@ public void setFabVisible(final boolean visible) { getActivity().runOnUiThread(() -> { if (visible) { mFabMain.show(); - viewThemeUtils.material.themeFAB(mFabMain); } else { mFabMain.hide(); } @@ -2099,6 +2093,7 @@ private void showFabWithBehavior(boolean visible) { * * @param enabled Desired visibility for the FAB. */ + @IonosCustomization public void setFabEnabled(final boolean enabled) { if (mFabMain == null) { // is not available in FolderPickerActivity @@ -2109,10 +2104,8 @@ public void setFabEnabled(final boolean enabled) { getActivity().runOnUiThread(() -> { if (enabled) { mFabMain.setEnabled(true); - viewThemeUtils.material.themeFAB(mFabMain); } else { mFabMain.setEnabled(false); - viewThemeUtils.material.themeFAB(mFabMain); } }); } diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/ProfileBottomSheetDialog.kt b/app/src/main/java/com/owncloud/android/ui/fragment/ProfileBottomSheetDialog.kt index 292818ec3df0..523e94c0fbc0 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/ProfileBottomSheetDialog.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/ProfileBottomSheetDialog.kt @@ -50,7 +50,7 @@ class ProfileBottomSheetDialog( window!!.setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) } - viewThemeUtils.platform.themeDialog(binding.root) + viewThemeUtils.ionos.platform.themeDialog(binding.root) binding.icon.tag = hoverCard.userId DisplayUtils.setAvatar( diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/QuickSharingPermissionsBottomSheetDialog.java b/app/src/main/java/com/owncloud/android/ui/fragment/QuickSharingPermissionsBottomSheetDialog.java index 820a6b0a7f22..dc7cab66d5de 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/QuickSharingPermissionsBottomSheetDialog.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/QuickSharingPermissionsBottomSheetDialog.java @@ -12,10 +12,10 @@ import android.os.Bundle; import android.view.View; -import android.view.ViewGroup; import com.google.android.material.bottomsheet.BottomSheetBehavior; import com.google.android.material.bottomsheet.BottomSheetDialog; +import com.ionos.annotation.IonosCustomization; import com.owncloud.android.R; import com.owncloud.android.databinding.QuickSharingPermissionsBottomSheetFragmentBinding; import com.owncloud.android.datamodel.QuickPermissionModel; @@ -57,16 +57,13 @@ public QuickSharingPermissionsBottomSheetDialog(FileActivity fileActivity, } @Override + @IonosCustomization("Remove custom window LayoutParams") protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); binding = QuickSharingPermissionsBottomSheetFragmentBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - if (getWindow() != null) { - getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); - } - - viewThemeUtils.platform.themeDialog(binding.getRoot()); + viewThemeUtils.ionos.platform.themeDialog(binding.getRoot()); setUpRecyclerView(); setOnShowListener(d -> diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/SimpleOCFileListBottomSheetDialog.java b/app/src/main/java/com/owncloud/android/ui/fragment/SimpleOCFileListBottomSheetDialog.java new file mode 100644 index 000000000000..13ca7c841094 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/fragment/SimpleOCFileListBottomSheetDialog.java @@ -0,0 +1,106 @@ +/* + * IONOS HiDrive Next - Android Client + * + * SPDX-FileCopyrightText: 2025 STRATO GmbH. + * SPDX-License-Identifier: GPL-2.0 + */ + +package com.owncloud.android.ui.fragment; + +import android.os.Bundle; +import android.view.View; + +import com.google.android.material.bottomsheet.BottomSheetDialog; +import com.nextcloud.android.common.ui.theme.utils.ColorRole; +import com.nextcloud.client.device.DeviceInfo; +import com.nextcloud.client.di.Injectable; +import com.nextcloud.client.documentscan.AppScanOptionalFeature; +import com.owncloud.android.R; +import com.owncloud.android.databinding.SimpleFileListActionsBottomSheetFragmentBinding; +import com.owncloud.android.ui.activity.FileActivity; +import com.owncloud.android.utils.theme.ThemeUtils; +import com.owncloud.android.utils.theme.ViewThemeUtils; + +/** + * Simple FAB menu {@link android.app.Dialog} styled as a bottom sheet for main actions. + */ +public class SimpleOCFileListBottomSheetDialog extends BottomSheetDialog implements Injectable { + + private SimpleFileListActionsBottomSheetFragmentBinding binding; + private final OCFileListBottomSheetActions actions; + private final DeviceInfo deviceInfo; + private final ThemeUtils themeUtils; + private final ViewThemeUtils viewThemeUtils; + + private final AppScanOptionalFeature appScanOptionalFeature; + + + public SimpleOCFileListBottomSheetDialog(FileActivity fileActivity, + OCFileListBottomSheetActions actions, + DeviceInfo deviceInfo, + ThemeUtils themeUtils, + ViewThemeUtils viewThemeUtils, + AppScanOptionalFeature appScanOptionalFeature) { + super(fileActivity); + this.actions = actions; + this.deviceInfo = deviceInfo; + this.themeUtils = themeUtils; + this.viewThemeUtils = viewThemeUtils; + this.appScanOptionalFeature = appScanOptionalFeature; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + binding = SimpleFileListActionsBottomSheetFragmentBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + + viewThemeUtils.ionos.platform.colorViewBackground(binding.getRoot(), ColorRole.SURFACE); + + binding.addToCloud.setText(getContext().getResources().getString(R.string.add_to_cloud, + themeUtils.getDefaultDisplayNameForRootFolder(getContext()))); + + if (!deviceInfo.hasCamera(getContext())) { + binding.menuDirectCameraUpload.setVisibility(View.GONE); + } + + setupClickListener(); + } + + private void setupClickListener() { + binding.menuMkdir.setOnClickListener(v -> { + actions.createFolder(); + dismiss(); + }); + + binding.menuUploadFromApp.setOnClickListener(v -> { + actions.uploadFromApp(); + dismiss(); + }); + + binding.menuDirectCameraUpload.setOnClickListener(v -> { + actions.directCameraUpload(); + dismiss(); + }); + + if (appScanOptionalFeature.isAvailable()) { + binding.menuScanDocUpload.setOnClickListener(v -> { + actions.scanDocUpload(); + dismiss(); + }); + } else { + binding.menuScanDocUpload.setVisibility(View.GONE); + } + + binding.menuUploadFiles.setOnClickListener(v -> { + actions.uploadFiles(); + dismiss(); + }); + } + + @Override + protected void onStop() { + super.onStop(); + binding = null; + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt index a158c8052948..520452f1b3fa 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/UnifiedSearchFragment.kt @@ -24,6 +24,7 @@ import androidx.core.view.updatePadding import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.GridLayoutManager +import com.ionos.annotation.IonosCustomization import com.nextcloud.client.account.CurrentAccountProvider import com.nextcloud.client.account.UserAccountManager import com.nextcloud.client.core.AsyncRunner @@ -170,12 +171,12 @@ class UnifiedSearchFragment : } } + @IonosCustomization private fun setupSearchView(item: MenuItem) { (item.actionView as? SearchView?)?.run { // Required to align with TextView width. // Because this fragment is opened with TextView onClick on the previous screen maxWidth = Integer.MAX_VALUE - viewThemeUtils.androidx.themeToolbarSearchView(this) setQuery(vm.query.value, false) setOnQueryTextListener(this@UnifiedSearchFragment) isIconified = false @@ -183,6 +184,7 @@ class UnifiedSearchFragment : } } + @IonosCustomization private fun setUpViewModel() { vm.searchResults.observe(this, this::onSearchResultChanged) vm.isLoading.observe(this) { loading -> @@ -208,9 +210,7 @@ class UnifiedSearchFragment : requireContext().getString(R.string.file_list_empty_headline_server_search) binding.emptyList.emptyListViewText.text = requireContext().getString(R.string.file_list_empty_unified_search_no_results) - binding.emptyList.emptyListIcon.setImageDrawable( - viewThemeUtils.platform.tintDrawable(requireContext(), R.drawable.ic_search_grey) - ) + binding.emptyList.emptyListIcon.setImageResource(R.drawable.ic_search) } } } diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/BackupFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/BackupFragment.kt index 52e7d03b695b..231573ae306c 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/BackupFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/BackupFragment.kt @@ -24,6 +24,7 @@ import android.widget.CompoundButton import android.widget.DatePicker import android.widget.Toast import androidx.core.app.ActivityCompat +import com.ionos.annotation.IonosCustomization import com.nextcloud.client.account.User import com.nextcloud.client.di.Injectable import com.nextcloud.client.jobs.BackgroundJobManager @@ -220,16 +221,8 @@ class BackupFragment : FileFragment(), OnDateSetListener, Injectable { } } + @IonosCustomization private fun applyUserColor() { - viewThemeUtils.androidx.colorSwitchCompat(binding.contacts) - viewThemeUtils.androidx.colorSwitchCompat(binding.calendar) - viewThemeUtils.androidx.colorSwitchCompat(binding.dailyBackup) - - viewThemeUtils.material.colorMaterialButtonPrimaryFilled(binding.backupNow) - viewThemeUtils.material.colorMaterialButtonPrimaryOutlined(binding.contactsDatepicker) - - viewThemeUtils.platform.colorTextView(binding.dataToBackUpTitle) - viewThemeUtils.platform.colorTextView(binding.backupSettingsTitle) } override fun onResume() { @@ -493,6 +486,7 @@ class BackupFragment : FileFragment(), OnDateSetListener, Injectable { } } + @IonosCustomization private fun openDate(savedDate: Calendar?) { val contactsPreferenceActivity = activity as ContactsPreferenceActivity? if (contactsPreferenceActivity == null) { @@ -529,10 +523,6 @@ class BackupFragment : FileFragment(), OnDateSetListener, Injectable { show() } - viewThemeUtils.platform.colorTextButtons( - datePickerDialog!!.getButton(DatePickerDialog.BUTTON_NEGATIVE), - datePickerDialog!!.getButton(DatePickerDialog.BUTTON_POSITIVE) - ) } else { DisplayUtils.showSnackMessage( requireView().findViewById(R.id.contacts_linear_layout), diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt index 7ebd4af61d3c..fa4f6a97b04c 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt @@ -21,6 +21,7 @@ import androidx.drawerlayout.widget.DrawerLayout import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.viewpager2.widget.ViewPager2 import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback +import com.ionos.annotation.IonosCustomization import com.nextcloud.client.account.User import com.nextcloud.client.di.Injectable import com.nextcloud.client.editimage.EditImageActivity @@ -52,6 +53,8 @@ import com.owncloud.android.ui.fragment.OCFileListFragment import com.owncloud.android.ui.preview.model.PreviewImageActivityState import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.MimeTypeUtil +import android.graphics.drawable.ColorDrawable +import androidx.activity.enableEdgeToEdge import edu.umd.cs.findbugs.annotations.SuppressFBWarnings import java.io.Serializable import javax.inject.Inject @@ -81,7 +84,9 @@ class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnR private var actionBar: ActionBar? = null + @IonosCustomization override fun onCreate(savedInstanceState: Bundle?) { + enableEdgeToEdge() super.onCreate(savedInstanceState) actionBar = supportActionBar @@ -103,8 +108,10 @@ class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnR val chosenFile = intent.getParcelableArgument(EXTRA_FILE, OCFile::class.java) updateActionBarTitleAndHomeButton(chosenFile) + viewThemeUtils.ionos.platform.themeSystemBars(this, getColor(R.color.preview_image_system_bars_color)) if (actionBar != null) { viewThemeUtils.files.setWhiteBackButton(this, actionBar!!) + actionBar?.setBackgroundDrawable(ColorDrawable(getColor(R.color.preview_image_system_bars_color))) actionBar?.setDisplayHomeAsUpEnabled(true) } @@ -472,10 +479,15 @@ class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnR fun toggleFullScreen() { if (fullScreenAnchorView == null) return - val visible = ( - fullScreenAnchorView!!.systemUiVisibility - and View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - ) == 0 + val visible = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + val insets = window.decorView.rootWindowInsets + insets.isVisible(WindowInsets.Type.statusBars()) + || insets.isVisible(WindowInsets.Type.navigationBars()) + } else { + @Suppress("DEPRECATION") + (fullScreenAnchorView!!.systemUiVisibility + and View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0 + } if (visible) { hideSystemUI(fullScreenAnchorView!!) @@ -521,6 +533,7 @@ class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnR private fun hideSystemUI(anchorView: View) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + actionBar?.hide() window.insetsController?.let { controller -> controller.hide(WindowInsets.Type.systemBars()) controller.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE @@ -540,6 +553,7 @@ class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnR private fun showSystemUI(anchorView: View) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + actionBar?.show() window.insetsController?.let { controller -> controller.show(WindowInsets.Type.systemBars()) diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.kt b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.kt index 96cc11fc2468..68ba5b82fcb0 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.kt @@ -43,6 +43,7 @@ import com.caverock.androidsvg.SVG import com.caverock.androidsvg.SVGParseException import com.github.chrisbanes.photoview.PhotoView import com.google.android.material.snackbar.Snackbar +import com.ionos.annotation.IonosCustomization import com.nextcloud.client.account.UserAccountManager import com.nextcloud.client.di.Injectable import com.nextcloud.client.jobs.BackgroundJobManager @@ -263,6 +264,7 @@ class PreviewImageFragment : FileFragment(), Injectable { } } + @IonosCustomization private fun adjustResizedImage(thumbnail: Bitmap?, width: Int, height: Int) { var resizedImage = getResizedBitmap(file, width, height) @@ -271,7 +273,7 @@ class PreviewImageFragment : FileFragment(), Injectable { binding.image.visibility = View.VISIBLE binding.emptyListView.visibility = View.GONE binding.emptyListProgress.visibility = View.GONE - binding.image.setBackgroundColor(resources.getColor(R.color.background_color_inverse)) + binding.image.setBackgroundColor(resources.getColor(R.color.preview_image_background)) bitmap = resizedImage } else { @@ -287,7 +289,7 @@ class PreviewImageFragment : FileFragment(), Injectable { containerActivity.storageManager, connectivityService, containerActivity.storageManager.user, - resources.getColor(R.color.background_color_inverse) + resources.getColor(R.color.preview_image_background) ) if (resizedImage == null) { resizedImage = thumbnail @@ -599,6 +601,7 @@ class PreviewImageFragment : FileFragment(), Injectable { } } + @IonosCustomization private fun showLoadedImage(result: LoadImage?) { val imageView = imageViewRef.get() val bitmap = result?.bitmap @@ -640,7 +643,7 @@ class PreviewImageFragment : FileFragment(), Injectable { val progressView = progressViewRef.get() progressView?.visibility = View.GONE - imageView.setBackgroundColor(resources.getColor(R.color.background_color_inverse)) + imageView.setBackgroundColor(resources.getColor(R.color.preview_image_background)) imageView.visibility = View.VISIBLE } } diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewMediaActivity.kt b/app/src/main/java/com/owncloud/android/ui/preview/PreviewMediaActivity.kt index 2b9b793b87ff..27c84716a08c 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewMediaActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewMediaActivity.kt @@ -36,8 +36,6 @@ import android.widget.FrameLayout import android.widget.LinearLayout import androidx.annotation.OptIn import androidx.annotation.StringRes -import androidx.appcompat.content.res.AppCompatResources -import androidx.core.graphics.drawable.DrawableCompat import androidx.core.view.ViewCompat import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat @@ -51,6 +49,7 @@ import androidx.media3.common.util.UnstableApi import androidx.media3.exoplayer.ExoPlayer import androidx.media3.ui.DefaultTimeBar import androidx.media3.ui.PlayerView +import com.ionos.annotation.IonosCustomization import com.nextcloud.client.account.User import com.nextcloud.client.account.UserAccountManager import com.nextcloud.client.di.Injectable @@ -258,7 +257,7 @@ class PreviewMediaActivity : viewThemeUtils.files.themeActionBar(this, it) } - viewThemeUtils.platform.themeStatusBar( + viewThemeUtils.ionos.platform.themeSystemBars( this ) } @@ -275,6 +274,7 @@ class PreviewMediaActivity : binding.emptyView.emptyListView.visibility = View.VISIBLE } + @IonosCustomization("ui bugfix") private fun setVideoErrorMessage(headline: String, @StringRes message: Int) { binding.emptyView.run { emptyListViewHeadline.text = headline @@ -282,8 +282,7 @@ class PreviewMediaActivity : emptyListIcon.setImageResource(R.drawable.file_movie) emptyListViewText.visibility = View.VISIBLE emptyListIcon.visibility = View.VISIBLE - - hideProgressLayout() + binding.progress.visibility = View.GONE } } @@ -312,6 +311,7 @@ class PreviewMediaActivity : } @Suppress("TooGenericExceptionCaught") + @IonosCustomization("Fixed nullpointer") private fun getThumbnail(storagePath: String?): Bitmap? { return try { MediaMetadataRetriever().run { @@ -319,7 +319,7 @@ class PreviewMediaActivity : BitmapFactory.decodeByteArray(embeddedPicture, 0, embeddedPicture?.size ?: 0) } } catch (t: Throwable) { - BitmapUtils.drawableToBitmap(genericThumbnail()) + genericThumbnail()?.let { BitmapUtils.drawableToBitmap(it) } } } @@ -333,15 +333,9 @@ class PreviewMediaActivity : binding.imagePreview.setImageDrawable(genericThumbnail()) } + @IonosCustomization("No generic thumbnail") private fun genericThumbnail(): Drawable? { - val result = AppCompatResources.getDrawable(this, R.drawable.logo) - result?.let { - if (!resources.getBoolean(R.bool.is_branded_client)) { - DrawableCompat.setTint(it, resources.getColor(R.color.primary, this.theme)) - } - } - - return result + return null } override fun onSaveInstanceState(outState: Bundle) { @@ -449,6 +443,7 @@ class PreviewMediaActivity : } @OptIn(markerClass = [UnstableApi::class]) + @IonosCustomization("Insets for media controller") private fun applyWindowInsets() { val playerView = binding.exoplayerView val exoControls = playerView.findViewById(R.id.exo_bottom_bar) @@ -467,10 +462,14 @@ class PreviewMediaActivity : exoControls.updateLayoutParams { bottomMargin = insets.bottom } + binding.mediaController.updateLayoutParams { + bottomMargin = insets.bottom + } exoProgress.updateLayoutParams { bottomMargin = insets.bottom + progressBottomMargin } exoControls.updatePadding(left = insets.left, right = insets.right) + binding.mediaController.updatePadding(left = insets.left, right = insets.right) exoProgress.updatePadding(left = insets.left, right = insets.right) binding.materialToolbar.updatePadding(left = insets.left, right = insets.right) WindowInsetsCompat.CONSUMED @@ -666,16 +665,19 @@ class PreviewMediaActivity : } @Suppress("TooGenericExceptionCaught") + @IonosCustomization("Better UX") private fun playVideo() { setupVideoView() - if (file.isDown) { - playVideoUri(file.storageUri) - } else { - try { - LoadStreamUrl(this, user, clientFactory).execute(file.localId) - } catch (e: Exception) { - Log_OC.e(TAG, "Loading stream url not possible: $e") + if(exoPlayer?.currentMediaItem == null) { + if (file.isDown) { + playVideoUri(file.storageUri) + } else { + try { + LoadStreamUrl(this, user, clientFactory).execute(file.localId) + } catch (e: Exception) { + Log_OC.e(TAG, "Loading stream url not possible: $e") + } } } } @@ -753,9 +755,13 @@ class PreviewMediaActivity : Log_OC.v(TAG, "onResume") } + @IonosCustomization("Stopping audio on latter lifecycle stage") override fun onDestroy() { Log_OC.v(TAG, "onDestroy") + stopAudio() + mediaPlayerServiceConnection?.unbind() + LocalBroadcastManager.getInstance(this).unregisterReceiver(mediaControlReceiver) super.onDestroy() @@ -765,6 +771,7 @@ class PreviewMediaActivity : } } + @IonosCustomization("Keeping audio/video playing in background") override fun onStop() { Log_OC.v(TAG, "onStop") @@ -774,9 +781,6 @@ class PreviewMediaActivity : } } - exoPlayer?.pause() - stopAudio() - mediaPlayerServiceConnection?.unbind() super.onStop() } diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewTextFileFragment.java b/app/src/main/java/com/owncloud/android/ui/preview/PreviewTextFileFragment.java index 410173d0fab6..17848a27c186 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewTextFileFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewTextFileFragment.java @@ -17,6 +17,7 @@ import android.widget.FrameLayout; import android.widget.TextView; +import com.ionos.annotation.IonosCustomization; import com.nextcloud.client.account.User; import com.nextcloud.ui.fileactions.FileActionsBottomSheet; import com.nextcloud.utils.extensions.BundleExtensionsKt; @@ -240,6 +241,7 @@ protected void onPostExecute(final StringWriter stringWriter) { * {@inheritDoc} */ @Override + @IonosCustomization public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.custom_menu_placeholder, menu); @@ -248,7 +250,6 @@ public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflat menuItem.setVisible(true); searchView = (SearchView) MenuItemCompat.getActionView(menuItem); searchView.setMaxWidth(Integer.MAX_VALUE); - viewThemeUtils.androidx.themeToolbarSearchView(searchView); if (searchOpen) { searchView.setIconified(false); diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewTextStringFragment.java b/app/src/main/java/com/owncloud/android/ui/preview/PreviewTextStringFragment.java index b9aa3f092a5f..975902a9052c 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewTextStringFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewTextStringFragment.java @@ -17,6 +17,7 @@ import android.view.ViewGroup; import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.ionos.annotation.IonosCustomization; import com.nextcloud.android.lib.richWorkspace.RichWorkspaceDirectEditingRemoteOperation; import com.nextcloud.utils.extensions.FileExtensionsKt; import com.owncloud.android.R; @@ -72,6 +73,7 @@ public void onSaveInstanceState(@NonNull Bundle outState) { } @Override + @IonosCustomization public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = super.onCreateView(inflater, container, savedInstanceState); @@ -85,7 +87,6 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, fabMain.setOnClickListener(v -> edit()); fabMain.setImageResource(R.drawable.ic_edit); - viewThemeUtils.material.themeFAB(fabMain); return view; } @@ -104,6 +105,7 @@ public void onStart() { * {@inheritDoc} */ @Override + @IonosCustomization public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); @@ -112,7 +114,6 @@ public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflat searchView = (SearchView) MenuItemCompat.getActionView(menuItem); searchView.setOnQueryTextListener(this); searchView.setMaxWidth(Integer.MAX_VALUE); - viewThemeUtils.androidx.themeToolbarSearchView(searchView); if (searchOpen) { searchView.setIconified(false); diff --git a/app/src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.kt b/app/src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.kt index 9097b149611d..9d89ffa0baa5 100644 --- a/app/src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.kt @@ -20,7 +20,9 @@ import androidx.activity.OnBackPressedCallback import androidx.annotation.VisibleForTesting import androidx.core.content.res.ResourcesCompat import androidx.recyclerview.widget.LinearLayoutManager +import com.google.android.material.button.MaterialButton import com.google.android.material.snackbar.Snackbar +import com.ionos.annotation.IonosCustomization import com.nextcloud.client.account.CurrentAccountProvider import com.nextcloud.client.di.Injectable import com.nextcloud.client.network.ClientFactory @@ -247,9 +249,10 @@ class TrashbinActivity : onBackPressedCallback.isEnabled = !isRoot } + @IonosCustomization override fun onSortingOrderChosen(selection: FileSortOrder?) { - val sortButton = findViewById(R.id.sort_button) - sortButton.setText(DisplayUtils.getSortOrderStringId(selection)) + val sortButton = findViewById(R.id.sort_button) + sortButton.setIconResource(DisplayUtils.getSortOrderIconRes(selection)) trashbinListAdapter?.setSortOrder(selection) } diff --git a/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java b/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java index 106916fb6e93..209546e1a61b 100644 --- a/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java +++ b/app/src/main/java/com/owncloud/android/utils/DisplayUtils.java @@ -57,10 +57,13 @@ import com.caverock.androidsvg.SVG; import com.elyeproj.loaderviewlibrary.LoaderImageView; import com.google.android.material.snackbar.Snackbar; +import com.ionos.annotation.IonosCustomization; import com.nextcloud.client.account.CurrentAccountProvider; import com.nextcloud.client.account.User; import com.nextcloud.client.network.ClientFactory; import com.nextcloud.client.preferences.AppPreferences; +import com.nextcloud.utils.BuildHelper; +import com.owncloud.android.BuildConfig; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.datamodel.ArbitraryDataProvider; @@ -101,6 +104,7 @@ import java.util.Map; import java.util.TimeZone; +import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; @@ -476,6 +480,7 @@ public static void setAvatar(@NonNull User user, @NonNull String userId, AvatarG * @param resources reference for density information * @param callContext which context is called to set the generated avatar */ + @IonosCustomization public static void setAvatar(@NonNull User user, @NonNull String userId, String displayName, @@ -488,6 +493,12 @@ public static void setAvatar(@NonNull User user, ((View) callContext).setContentDescription(String.valueOf(user.toPlatformAccount().hashCode())); } + if (BuildHelper.GPLAY.equals(BuildConfig.FLAVOR)) { + Drawable avatar = ResourcesCompat.getDrawable(resources, R.drawable.account_circle_white, null); + listener.avatarGenerated(avatar, callContext); + return; + } + ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(context); final String accountName = user.getAccountName(); @@ -817,21 +828,22 @@ static public void openSortingOrderDialogFragment(FragmentManager supportFragmen SortingOrderDialogFragment.newInstance(sortOrder).show(fragmentTransaction, SORTING_ORDER_FRAGMENT); } - public static @StringRes int getSortOrderStringId(FileSortOrder sortOrder) { + @IonosCustomization + public static @DrawableRes int getSortOrderIconRes(FileSortOrder sortOrder) { switch (sortOrder.name) { case SORT_Z_TO_A_ID: - return R.string.menu_item_sort_by_name_z_a; + return R.drawable.ic_alphabetical_desc; case SORT_NEW_TO_OLD_ID: - return R.string.menu_item_sort_by_date_newest_first; + return R.drawable.ic_modification_desc; case SORT_OLD_TO_NEW_ID: - return R.string.menu_item_sort_by_date_oldest_first; + return R.drawable.ic_modification_asc; case SORT_BIG_TO_SMALL_ID: - return R.string.menu_item_sort_by_size_biggest_first; + return R.drawable.ic_size_desc; case SORT_SMALL_TO_BIG_ID: - return R.string.menu_item_sort_by_size_smallest_first; + return R.drawable.ic_size_asc; case SORT_A_TO_Z_ID: default: - return R.string.menu_item_sort_by_name_a_z; + return R.drawable.ic_alphabetical_asc; } } diff --git a/app/src/main/java/com/owncloud/android/utils/DrawableUtil.kt b/app/src/main/java/com/owncloud/android/utils/DrawableUtil.kt index c8aa15fb5ac6..0fffc28f42d9 100644 --- a/app/src/main/java/com/owncloud/android/utils/DrawableUtil.kt +++ b/app/src/main/java/com/owncloud/android/utils/DrawableUtil.kt @@ -7,10 +7,11 @@ */ package com.owncloud.android.utils -import android.graphics.Rect import android.graphics.drawable.Drawable import android.graphics.drawable.LayerDrawable +import android.view.Gravity import androidx.core.graphics.drawable.DrawableCompat +import com.ionos.annotation.IonosCustomization class DrawableUtil { @@ -20,14 +21,11 @@ class DrawableUtil { return drawable } + @IonosCustomization fun addDrawableAsOverlay(backgroundDrawable: Drawable, overlayDrawable: Drawable): LayerDrawable { - val overlayBounds = Rect() - val overlayIconSize = backgroundDrawable.intrinsicWidth / 2 - val topMargin = overlayIconSize.div(2) - overlayBounds.set(overlayIconSize, overlayIconSize + topMargin, overlayIconSize, overlayIconSize) - - val layerDrawable = LayerDrawable(arrayOf(backgroundDrawable, overlayDrawable)) - layerDrawable.setLayerInset(1, overlayBounds.left, overlayBounds.top, overlayBounds.right, overlayBounds.bottom) - return layerDrawable + return LayerDrawable(arrayOf(backgroundDrawable, overlayDrawable)).apply { + setLayerSize(1, overlayDrawable.intrinsicWidth, overlayDrawable.intrinsicHeight) + setLayerGravity(1, Gravity.CENTER) + } } } diff --git a/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java b/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java index 7292b1400edb..dfd383c3e8ac 100644 --- a/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java +++ b/app/src/main/java/com/owncloud/android/utils/DrawerMenuUtil.java @@ -10,6 +10,7 @@ import android.content.res.Resources; import android.view.Menu; +import com.ionos.annotation.IonosCustomization; import com.nextcloud.client.account.User; import com.owncloud.android.MainApp; import com.owncloud.android.R; @@ -78,6 +79,16 @@ public static void setupHomeMenuItem(Menu menu, Resources resources) { } } + @IonosCustomization + public static void removePersonalFiles(Menu menu) { + removeMenuItem(menu, R.id.nav_personal_files); + } + + @IonosCustomization + public static void removeNotifications(Menu menu) { + removeMenuItem(menu, R.id.nav_notifications); + } + private static void removeMenuItem(Menu menu, int... menuIds) { if (menuIds != null) { for (int menuId : menuIds) { diff --git a/app/src/main/java/com/owncloud/android/utils/MimeTypeUtil.java b/app/src/main/java/com/owncloud/android/utils/MimeTypeUtil.java index 5e11ec467c05..91c50a0f0a24 100644 --- a/app/src/main/java/com/owncloud/android/utils/MimeTypeUtil.java +++ b/app/src/main/java/com/owncloud/android/utils/MimeTypeUtil.java @@ -16,7 +16,7 @@ import android.net.Uri; import android.webkit.MimeTypeMap; -import com.nextcloud.android.common.ui.theme.utils.ColorRole; +import com.ionos.annotation.IonosCustomization; import com.owncloud.android.R; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.lib.resources.files.model.ServerFileInterface; @@ -88,6 +88,7 @@ private MimeTypeUtil() { * @param filename Name, with extension. * @return Drawable of an image resource. */ + @IonosCustomization("Custom icon tint") public static Drawable getFileTypeIcon(String mimetype, String filename, Context context, @@ -96,8 +97,8 @@ public static Drawable getFileTypeIcon(String mimetype, int iconId = MimeTypeUtil.getFileTypeIconId(mimetype, filename); Drawable icon = ContextCompat.getDrawable(context, iconId); - if (R.drawable.file_zip == iconId) { - viewThemeUtils.platform.tintPrimaryDrawable(context, icon); + if (icon != null) { + icon.setTint(context.getColor(R.color.filelist_file_icon_color)); } return icon; @@ -124,14 +125,16 @@ public static int getFileTypeIconId(String mimetype, String filename) { return determineIconIdByMimeTypeList(possibleMimeTypes); } + @IonosCustomization("Custom icon tint") public static Drawable getDefaultFolderIcon(Context context, ViewThemeUtils viewThemeUtils) { Drawable drawable = ContextCompat.getDrawable(context, R.drawable.folder); assert(drawable != null); - viewThemeUtils.platform.tintDrawable(context, drawable, ColorRole.PRIMARY); + drawable.setTint(context.getColor(R.color.filelist_file_icon_color)); return drawable; } + @IonosCustomization("Remove custom overlay color for dark mode") public static LayerDrawable getFileIcon(Boolean isDarkModeActive, Integer overlayIconId, Context context, ViewThemeUtils viewThemeUtils) { Drawable folderDrawable = getDefaultFolderIcon(context, viewThemeUtils); assert(folderDrawable != null); @@ -147,10 +150,6 @@ public static LayerDrawable getFileIcon(Boolean isDarkModeActive, Integer overla Drawable overlayDrawable = ContextCompat.getDrawable(context, overlayIconId); assert(overlayDrawable != null); - if (isDarkModeActive) { - overlayDrawable = drawableUtil.changeColor(overlayDrawable, R.color.dark); - } - return drawableUtil.addDrawableAsOverlay(folderDrawable, overlayDrawable); } diff --git a/app/src/main/java/com/owncloud/android/utils/theme/FilesSpecificViewThemeUtils.kt b/app/src/main/java/com/owncloud/android/utils/theme/FilesSpecificViewThemeUtils.kt index 41f13b37156f..5508572164b9 100644 --- a/app/src/main/java/com/owncloud/android/utils/theme/FilesSpecificViewThemeUtils.kt +++ b/app/src/main/java/com/owncloud/android/utils/theme/FilesSpecificViewThemeUtils.kt @@ -23,6 +23,7 @@ import androidx.annotation.StringRes import androidx.appcompat.app.ActionBar import androidx.core.content.res.ResourcesCompat import com.google.android.material.card.MaterialCardView +import com.ionos.annotation.IonosCustomization import com.nextcloud.android.common.ui.color.ColorUtil import com.nextcloud.android.common.ui.theme.MaterialSchemes import com.nextcloud.android.common.ui.theme.ViewThemeUtilsBase @@ -43,17 +44,9 @@ class FilesSpecificViewThemeUtils @Inject constructor( private val androidXViewThemeUtils: AndroidXViewThemeUtils ) : ViewThemeUtilsBase(schemes) { // not ported to common lib because PreferenceCategory is deprecated + @IonosCustomization fun themePreferenceCategory(category: PreferenceCategory) { - withScheme(category.context) { - val text: Spannable = SpannableString(category.title) - text.setSpan( - ForegroundColorSpan(it.primary), - 0, - text.length, - Spannable.SPAN_INCLUSIVE_INCLUSIVE - ) - category.title = text - } + // Do nothing } fun createAvatar(type: ShareType?, avatar: ImageView, context: Context) { @@ -132,19 +125,11 @@ class FilesSpecificViewThemeUtils @Inject constructor( */ // TODO move back arrow resource to lib and use lib method directly? @JvmOverloads + @IonosCustomization fun themeActionBar(context: Context, actionBar: ActionBar, title: String, isMenu: Boolean = false) { val icon = getHomeAsUpIcon(isMenu) - val backArrow = ResourcesCompat.getDrawable( - context.resources, - icon, - null - )!! - androidXViewThemeUtils.themeActionBar( - context, - actionBar, - title, - backArrow - ) + actionBar.setHomeAsUpIndicator(icon) + actionBar.title = title } /** @@ -165,13 +150,10 @@ class FilesSpecificViewThemeUtils @Inject constructor( * Colors actionbar background and back arrow but not the title */ @JvmOverloads + @IonosCustomization fun themeActionBar(context: Context, actionBar: ActionBar, isMenu: Boolean = false) { - val backArrow = ResourcesCompat.getDrawable( - context.resources, - getHomeAsUpIcon(isMenu), - null - )!! - androidXViewThemeUtils.themeActionBar(context, actionBar, backArrow) + val icon = getHomeAsUpIcon(isMenu) + actionBar.setHomeAsUpIndicator(icon) } fun themeTemplateCardView(cardView: MaterialCardView) { diff --git a/app/src/main/java/com/owncloud/android/utils/theme/ViewThemeUtils.kt b/app/src/main/java/com/owncloud/android/utils/theme/ViewThemeUtils.kt index 5a09ea8d69a5..7faac60931ca 100644 --- a/app/src/main/java/com/owncloud/android/utils/theme/ViewThemeUtils.kt +++ b/app/src/main/java/com/owncloud/android/utils/theme/ViewThemeUtils.kt @@ -7,6 +7,10 @@ */ package com.owncloud.android.utils.theme +import com.ionos.annotation.IonosCustomization +import com.ionos.utils.IonosDialogViewThemeUtils +import com.ionos.utils.IonosMaterialViewThemeUtils +import com.ionos.utils.IonosViewThemeUtils import com.nextcloud.android.common.ui.color.ColorUtil import com.nextcloud.android.common.ui.theme.MaterialSchemes import com.nextcloud.android.common.ui.theme.ViewThemeUtilsBase @@ -27,17 +31,21 @@ class ViewThemeUtils @Inject constructor( val platform = AndroidViewThemeUtils(schemes, colorUtil) @JvmField - val material = MaterialViewThemeUtils(schemes, colorUtil) + val material = IonosMaterialViewThemeUtils(MaterialViewThemeUtils(schemes, colorUtil)) @JvmField val androidx = AndroidXViewThemeUtils(schemes, platform) @JvmField - val dialog = DialogViewThemeUtils(schemes) + val dialog = IonosDialogViewThemeUtils(DialogViewThemeUtils(schemes)) @JvmField val files = FilesSpecificViewThemeUtils(schemes, colorUtil, platform, androidx) + @JvmField + @IonosCustomization + val ionos = IonosViewThemeUtils(platform) + class Factory @Inject constructor( private val schemesProvider: MaterialSchemesProvider, private val colorUtil: ColorUtil diff --git a/app/src/main/res/anim/fade_in.xml b/app/src/main/res/anim/fade_in.xml new file mode 100644 index 000000000000..a21744ff2499 --- /dev/null +++ b/app/src/main/res/anim/fade_in.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/anim/fade_out.xml b/app/src/main/res/anim/fade_out.xml new file mode 100644 index 000000000000..a2b5526cb1ec --- /dev/null +++ b/app/src/main/res/anim/fade_out.xml @@ -0,0 +1,12 @@ + + + + diff --git a/app/src/main/res/color/fab_background_tint.xml b/app/src/main/res/color/fab_background_tint.xml new file mode 100644 index 000000000000..debb631f48e9 --- /dev/null +++ b/app/src/main/res/color/fab_background_tint.xml @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/background_splash.xml b/app/src/main/res/drawable/background_splash.xml new file mode 100644 index 000000000000..e27a0271888a --- /dev/null +++ b/app/src/main/res/drawable/background_splash.xml @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/app/src/main/res/drawable/grid_mode_item_background.xml b/app/src/main/res/drawable/grid_mode_item_background.xml new file mode 100644 index 000000000000..a35817881607 --- /dev/null +++ b/app/src/main/res/drawable/grid_mode_item_background.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/app/src/main/res/drawable/grid_mode_selected_item_background.xml b/app/src/main/res/drawable/grid_mode_selected_item_background.xml new file mode 100644 index 000000000000..88b9877084b8 --- /dev/null +++ b/app/src/main/res/drawable/grid_mode_selected_item_background.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_data_protection.xml b/app/src/main/res/drawable/ic_data_protection.xml new file mode 100644 index 000000000000..72c7887423de --- /dev/null +++ b/app/src/main/res/drawable/ic_data_protection.xml @@ -0,0 +1,17 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_file_action_cancel_sync.xml b/app/src/main/res/drawable/ic_file_action_cancel_sync.xml new file mode 100644 index 000000000000..c7f0db0ca5d3 --- /dev/null +++ b/app/src/main/res/drawable/ic_file_action_cancel_sync.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_file_action_download_file.xml b/app/src/main/res/drawable/ic_file_action_download_file.xml new file mode 100644 index 000000000000..4b1561b1b431 --- /dev/null +++ b/app/src/main/res/drawable/ic_file_action_download_file.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_file_action_edit.xml b/app/src/main/res/drawable/ic_file_action_edit.xml new file mode 100644 index 000000000000..e2bcc65afb5c --- /dev/null +++ b/app/src/main/res/drawable/ic_file_action_edit.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_file_action_export_file.xml b/app/src/main/res/drawable/ic_file_action_export_file.xml new file mode 100644 index 000000000000..e6670404593a --- /dev/null +++ b/app/src/main/res/drawable/ic_file_action_export_file.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_file_action_favorite.xml b/app/src/main/res/drawable/ic_file_action_favorite.xml new file mode 100644 index 000000000000..271799f8e9e2 --- /dev/null +++ b/app/src/main/res/drawable/ic_file_action_favorite.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_file_action_lock_file.xml b/app/src/main/res/drawable/ic_file_action_lock_file.xml new file mode 100644 index 000000000000..eaeef744157c --- /dev/null +++ b/app/src/main/res/drawable/ic_file_action_lock_file.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_file_action_move_or_copy.xml b/app/src/main/res/drawable/ic_file_action_move_or_copy.xml new file mode 100644 index 000000000000..388986ce246c --- /dev/null +++ b/app/src/main/res/drawable/ic_file_action_move_or_copy.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_file_action_open_file_with.xml b/app/src/main/res/drawable/ic_file_action_open_file_with.xml new file mode 100644 index 000000000000..388986ce246c --- /dev/null +++ b/app/src/main/res/drawable/ic_file_action_open_file_with.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_file_action_pin_to_homescreen.xml b/app/src/main/res/drawable/ic_file_action_pin_to_homescreen.xml new file mode 100644 index 000000000000..cd3fb9e8fb92 --- /dev/null +++ b/app/src/main/res/drawable/ic_file_action_pin_to_homescreen.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_file_action_remove_file.xml b/app/src/main/res/drawable/ic_file_action_remove_file.xml new file mode 100644 index 000000000000..b72c7a747d03 --- /dev/null +++ b/app/src/main/res/drawable/ic_file_action_remove_file.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_file_action_rename_file.xml b/app/src/main/res/drawable/ic_file_action_rename_file.xml new file mode 100644 index 000000000000..72ff8ee453d5 --- /dev/null +++ b/app/src/main/res/drawable/ic_file_action_rename_file.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_file_action_see_details.xml b/app/src/main/res/drawable/ic_file_action_see_details.xml new file mode 100644 index 000000000000..ef7bb988dfb8 --- /dev/null +++ b/app/src/main/res/drawable/ic_file_action_see_details.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_file_action_select_all.xml b/app/src/main/res/drawable/ic_file_action_select_all.xml new file mode 100644 index 000000000000..b017bf913225 --- /dev/null +++ b/app/src/main/res/drawable/ic_file_action_select_all.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_file_action_select_none.xml b/app/src/main/res/drawable/ic_file_action_select_none.xml new file mode 100644 index 000000000000..329e4d1b7e7e --- /dev/null +++ b/app/src/main/res/drawable/ic_file_action_select_none.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_file_action_set_as_wallpaper.xml b/app/src/main/res/drawable/ic_file_action_set_as_wallpaper.xml new file mode 100644 index 000000000000..41755ab3be06 --- /dev/null +++ b/app/src/main/res/drawable/ic_file_action_set_as_wallpaper.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_file_action_set_encrypted.xml b/app/src/main/res/drawable/ic_file_action_set_encrypted.xml new file mode 100644 index 000000000000..b7d8b3c97021 --- /dev/null +++ b/app/src/main/res/drawable/ic_file_action_set_encrypted.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_file_action_share_file.xml b/app/src/main/res/drawable/ic_file_action_share_file.xml new file mode 100644 index 000000000000..1a9d21d9b281 --- /dev/null +++ b/app/src/main/res/drawable/ic_file_action_share_file.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_file_action_stream_media.xml b/app/src/main/res/drawable/ic_file_action_stream_media.xml new file mode 100644 index 000000000000..be328e566a66 --- /dev/null +++ b/app/src/main/res/drawable/ic_file_action_stream_media.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_file_action_sync_file.xml b/app/src/main/res/drawable/ic_file_action_sync_file.xml new file mode 100644 index 000000000000..7054f44c69be --- /dev/null +++ b/app/src/main/res/drawable/ic_file_action_sync_file.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_file_action_unlock_file.xml b/app/src/main/res/drawable/ic_file_action_unlock_file.xml new file mode 100644 index 000000000000..8b84787751ec --- /dev/null +++ b/app/src/main/res/drawable/ic_file_action_unlock_file.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_file_action_unset_encrypted.xml b/app/src/main/res/drawable/ic_file_action_unset_encrypted.xml new file mode 100644 index 000000000000..9a17c0e2b9dd --- /dev/null +++ b/app/src/main/res/drawable/ic_file_action_unset_encrypted.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_file_action_unset_favorite.xml b/app/src/main/res/drawable/ic_file_action_unset_favorite.xml new file mode 100644 index 000000000000..23b75c5bafe3 --- /dev/null +++ b/app/src/main/res/drawable/ic_file_action_unset_favorite.xml @@ -0,0 +1,10 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_filelist_shared_via_link.xml b/app/src/main/res/drawable/ic_filelist_shared_via_link.xml new file mode 100644 index 000000000000..7c81f174d1d5 --- /dev/null +++ b/app/src/main/res/drawable/ic_filelist_shared_via_link.xml @@ -0,0 +1,29 @@ + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_filelist_shared_via_users.xml b/app/src/main/res/drawable/ic_filelist_shared_via_users.xml new file mode 100644 index 000000000000..bfd399f13b6e --- /dev/null +++ b/app/src/main/res/drawable/ic_filelist_shared_via_users.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_filelist_unshared.xml b/app/src/main/res/drawable/ic_filelist_unshared.xml new file mode 100644 index 000000000000..64b2f6d3af01 --- /dev/null +++ b/app/src/main/res/drawable/ic_filelist_unshared.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_filelist_unshared_grid_mode.xml b/app/src/main/res/drawable/ic_filelist_unshared_grid_mode.xml new file mode 100644 index 000000000000..64b2f6d3af01 --- /dev/null +++ b/app/src/main/res/drawable/ic_filelist_unshared_grid_mode.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/main/res/drawable/rounded_rect_4dp.xml b/app/src/main/res/drawable/rounded_rect_4dp.xml new file mode 100644 index 000000000000..1952e3c9f856 --- /dev/null +++ b/app/src/main/res/drawable/rounded_rect_4dp.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/app/src/main/res/layout/activity_data_protection.xml b/app/src/main/res/layout/activity_data_protection.xml new file mode 100644 index 000000000000..662d0f05fe0d --- /dev/null +++ b/app/src/main/res/layout/activity_data_protection.xml @@ -0,0 +1,24 @@ + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_data_protection_detail_page.xml b/app/src/main/res/layout/activity_data_protection_detail_page.xml new file mode 100644 index 000000000000..459f10840155 --- /dev/null +++ b/app/src/main/res/layout/activity_data_protection_detail_page.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_data_protection_overview_page.xml b/app/src/main/res/layout/activity_data_protection_overview_page.xml new file mode 100644 index 000000000000..7363d67193dd --- /dev/null +++ b/app/src/main/res/layout/activity_data_protection_overview_page.xml @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_edit_image.xml b/app/src/main/res/layout/activity_edit_image.xml index 4b11869ee501..a67156844e45 100644 --- a/app/src/main/res/layout/activity_edit_image.xml +++ b/app/src/main/res/layout/activity_edit_image.xml @@ -10,13 +10,13 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/black"> + android:background="@color/edit_image_background"> + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/dialog_choose_account.xml b/app/src/main/res/layout/dialog_choose_account.xml index 66e05389de49..53e7b9fa2693 100644 --- a/app/src/main/res/layout/dialog_choose_account.xml +++ b/app/src/main/res/layout/dialog_choose_account.xml @@ -46,10 +46,12 @@ android:textAlignment="textStart" android:textAllCaps="false" android:textColor="@color/fontAppbar" + android:visibility="gone" app:icon="@drawable/ic_edit" app:iconGravity="start" app:iconPadding="22dp" - app:iconTint="@color/fontAppbar" /> + app:iconTint="@color/fontAppbar" + app:ionosCustomization="" /> diff --git a/app/src/main/res/layout/empty_list.xml b/app/src/main/res/layout/empty_list.xml index 5f1aea457a05..a0d1da1af082 100644 --- a/app/src/main/res/layout/empty_list.xml +++ b/app/src/main/res/layout/empty_list.xml @@ -43,6 +43,7 @@ android:paddingTop="@dimen/standard_padding" android:paddingBottom="@dimen/standard_half_padding" android:text="@string/file_list_loading" + android:textColor="@color/text_color" android:textSize="26sp" /> diff --git a/app/src/main/res/layout/file_details_sharing_process_fragment.xml b/app/src/main/res/layout/file_details_sharing_process_fragment.xml index 220e61e96532..3c974bb5af2b 100644 --- a/app/src/main/res/layout/file_details_sharing_process_fragment.xml +++ b/app/src/main/res/layout/file_details_sharing_process_fragment.xml @@ -53,6 +53,7 @@ android:id="@+id/share_process_permission_read_only" android:layout_width="match_parent" android:layout_height="wrap_content" + android:textColor="@color/text_color" android:minHeight="@dimen/minimum_size_for_touchable_area" android:text="@string/link_share_view_only" /> @@ -60,6 +61,7 @@ android:id="@+id/share_process_permission_upload_editing" android:layout_width="match_parent" android:layout_height="wrap_content" + android:textColor="@color/text_color" android:minHeight="@dimen/minimum_size_for_touchable_area" android:text="@string/link_share_allow_upload_and_editing" /> @@ -67,6 +69,7 @@ android:id="@+id/share_process_permission_file_drop" android:layout_width="match_parent" android:layout_height="wrap_content" + android:textColor="@color/text_color" android:minHeight="@dimen/minimum_size_for_touchable_area" android:text="@string/link_share_file_drop" /> @@ -90,6 +93,7 @@ android:layout_height="wrap_content" android:minHeight="@dimen/minimum_size_for_touchable_area" android:text="@string/allow_resharing" + android:textColor="@color/text_color" android:visibility="gone" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/share_process_advance_permission_title" @@ -101,6 +105,7 @@ android:layout_height="wrap_content" android:minHeight="@dimen/minimum_size_for_touchable_area" android:text="@string/share_no_password_title" + android:textColor="@color/text_color" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/share_process_allow_resharing_checkbox" /> @@ -137,6 +142,7 @@ android:layout_marginTop="@dimen/standard_half_margin" android:minHeight="@dimen/minimum_size_for_touchable_area" android:text="@string/share_no_expiration_date_label" + android:textColor="@color/text_color" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/share_process_enter_password_container" /> @@ -171,6 +177,7 @@ android:layout_marginTop="@dimen/standard_half_margin" android:minHeight="@dimen/minimum_size_for_touchable_area" android:text="@string/share_via_link_hide_download" + android:textColor="@color/text_color" android:visibility="gone" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" @@ -184,6 +191,7 @@ android:layout_marginTop="@dimen/standard_half_margin" android:minHeight="@dimen/minimum_size_for_touchable_area" android:text="@string/link_name" + android:textColor="@color/text_color" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/share_process_hide_download_checkbox" /> @@ -271,15 +279,15 @@ @@ -290,9 +298,13 @@ android:layout_marginStart="@dimen/standard_half_margin" android:layout_marginEnd="@dimen/standard_margin" android:layout_marginBottom="@dimen/standard_margin" + android:ellipsize="end" + android:maxLines="2" android:text="@string/common_next" - android:theme="@style/Widget.Material3.Button.IconButton.Filled" + android:theme="@style/Theme.Dialog.IconButton.Filled" + android:textColor="@color/filled_button_text_color" app:cornerRadius="@dimen/button_corner_radius" + app:ionosCustomization="Limit max lines number to 2" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" /> diff --git a/app/src/main/res/layout/file_thumbnail.xml b/app/src/main/res/layout/file_thumbnail.xml index 6f318d47e9b3..76b528104a57 100644 --- a/app/src/main/res/layout/file_thumbnail.xml +++ b/app/src/main/res/layout/file_thumbnail.xml @@ -19,6 +19,16 @@ android:layout_height="@dimen/file_icon_size" android:layout_gravity="center"> + + diff --git a/app/src/main/res/layout/files_folder_picker.xml b/app/src/main/res/layout/files_folder_picker.xml index f1d4f53ebb5d..ddbdc84a720e 100644 --- a/app/src/main/res/layout/files_folder_picker.xml +++ b/app/src/main/res/layout/files_folder_picker.xml @@ -51,7 +51,7 @@ @@ -31,6 +32,8 @@ android:layout_height="wrap_content" android:layout_marginTop="@dimen/standard_margin" android:layout_marginBottom="@dimen/standard_margin" + android:textStyle="bold" + app:ionosCustomization="bold text" android:textAppearance="?android:attr/textAppearanceLarge" tools:text="2016" /> diff --git a/app/src/main/res/layout/grid_image.xml b/app/src/main/res/layout/grid_image.xml index 69e064ac87f4..7cab438f410e 100644 --- a/app/src/main/res/layout/grid_image.xml +++ b/app/src/main/res/layout/grid_image.xml @@ -23,6 +23,20 @@ android:layout_width="@dimen/grid_container_width" android:layout_height="@dimen/grid_container_height"> + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/log_entry_list_item.xml b/app/src/main/res/layout/log_entry_list_item.xml index 969711013c5b..ce6cf8f44fc0 100644 --- a/app/src/main/res/layout/log_entry_list_item.xml +++ b/app/src/main/res/layout/log_entry_list_item.xml @@ -20,6 +20,7 @@ android:ellipsize="end" android:lines="1" android:textStyle="bold" + android:textColor="@color/text_color" tools:text="@tools:sample/lorem/random" /> + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/search_users_groups_layout.xml b/app/src/main/res/layout/search_users_groups_layout.xml index f0b30be55872..21f1237cf328 100644 --- a/app/src/main/res/layout/search_users_groups_layout.xml +++ b/app/src/main/res/layout/search_users_groups_layout.xml @@ -30,7 +30,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/searchView" - android:hint="@string/share_search" + android:hint="@string/ionos_share_search" style="@style/ownCloud.SearchView"/> + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/simple_file_list_actions_bottom_sheet_fragment.xml b/app/src/main/res/layout/simple_file_list_actions_bottom_sheet_fragment.xml new file mode 100644 index 000000000000..88b1e407a71c --- /dev/null +++ b/app/src/main/res/layout/simple_file_list_actions_bottom_sheet_fragment.xml @@ -0,0 +1,208 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/simple_spinner_item.xml b/app/src/main/res/layout/simple_spinner_item.xml new file mode 100644 index 000000000000..accb287cbdde --- /dev/null +++ b/app/src/main/res/layout/simple_spinner_item.xml @@ -0,0 +1,15 @@ + + + + diff --git a/app/src/main/res/layout/storage_path_item.xml b/app/src/main/res/layout/storage_path_item.xml index 2b506d49cede..3415ebc58674 100644 --- a/app/src/main/res/layout/storage_path_item.xml +++ b/app/src/main/res/layout/storage_path_item.xml @@ -15,6 +15,9 @@ android:gravity="center|start" android:paddingBottom="@dimen/standard_half_padding" android:text="@string/menu_item_sort_by_name_z_a" + android:textColor="@color/text_color" app:icon="@drawable/ic_user" app:iconPadding="@dimen/standard_padding" + app:iconTint="@color/default_icon_color" + app:ionosCustomization="Custom color" tools:text="DCIM" /> diff --git a/app/src/main/res/layout/synced_folders_settings_layout.xml b/app/src/main/res/layout/synced_folders_settings_layout.xml index 378629b73c6e..2e044b9671d8 100644 --- a/app/src/main/res/layout/synced_folders_settings_layout.xml +++ b/app/src/main/res/layout/synced_folders_settings_layout.xml @@ -442,7 +442,7 @@ @@ -465,7 +465,7 @@ android:id="@+id/btnPositive" android:layout_width="wrap_content" android:text="@string/common_save" - style="@style/Widget.Material3.Button.TonalButton" + style="@style/Theme.Dialog.IconButton.Filled.Tonal" android:layout_height="wrap_content" android:layout_weight="1"/> diff --git a/app/src/main/res/layout/upload_files_layout.xml b/app/src/main/res/layout/upload_files_layout.xml index 4012193eb6a9..2c4209c22e3d 100644 --- a/app/src/main/res/layout/upload_files_layout.xml +++ b/app/src/main/res/layout/upload_files_layout.xml @@ -78,7 +78,7 @@ android:layout_marginEnd="@dimen/standard_half_margin" android:layout_weight="1" android:text="@string/common_cancel" - style="@style/OutlinedButton" + style="@style/Theme.Dialog.Button.OutlinedButton" app:cornerRadius="@dimen/button_corner_radius" /> diff --git a/app/src/main/res/layout/view_authorization_method.xml b/app/src/main/res/layout/view_authorization_method.xml new file mode 100644 index 000000000000..51ea5477db49 --- /dev/null +++ b/app/src/main/res/layout/view_authorization_method.xml @@ -0,0 +1,25 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/activity_folder_picker.xml b/app/src/main/res/menu/activity_folder_picker.xml index 3e6fdcebbf2a..21a6ab87f424 100644 --- a/app/src/main/res/menu/activity_folder_picker.xml +++ b/app/src/main/res/menu/activity_folder_picker.xml @@ -23,6 +23,6 @@ android:icon="@drawable/ic_action_create_dir" android:orderInCategory="1" android:title="@string/actionbar_mkdir" - app:iconTint="?attr/colorOnSurface" + app:ionosCustomization="Remove tint" app:showAsAction="ifRoom"/> diff --git a/app/src/main/res/menu/activity_receive_external_files.xml b/app/src/main/res/menu/activity_receive_external_files.xml index 4c22d95237bd..ed4a7894823e 100644 --- a/app/src/main/res/menu/activity_receive_external_files.xml +++ b/app/src/main/res/menu/activity_receive_external_files.xml @@ -27,7 +27,7 @@ android:icon="@drawable/ic_action_create_dir" android:orderInCategory="1" android:title="@string/actionbar_mkdir" - app:iconTint="?attr/colorOnSurface" + app:ionosCustomization="Remove tint" app:showAsAction="never"/> diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 954a57cac279..b5be74127cc3 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -103,6 +103,8 @@ حدث إشكال عند معالجة طلبك للدخول. يُرجى المحاولة مرةً أخرى. يرجى إكمال عملية تسجيل الدخول في المتصفح الخاص بك ترك في مجلد الأصل، بسبب كونه للقرائة فقط + تمّ تغيير سلوك التحميل التلقائي + بسبب القيود الجديدة المفروضة من قِبل شركة قوقل، فإن خاصية التحميل التلقائي سوف لن يمكنها بعدُ الحذف التلقائي للملفات المرفوعة. قم بالرفع عبر شبكة لاسلكية غير محدودة البيانات فقط /رفع تلقائي إعداد diff --git a/app/src/main/res/values-b+en+001/strings.xml b/app/src/main/res/values-b+en+001/strings.xml index c4d07977bc58..9d2a448b6f11 100644 --- a/app/src/main/res/values-b+en+001/strings.xml +++ b/app/src/main/res/values-b+en+001/strings.xml @@ -103,6 +103,8 @@ There was an issue processing your login request. Please try again later. Please complete login process in your browser kept in original folder, as it is readonly + Auto upload behaviour changed + Due to new restrictions imposed by Google, the auto upload feature will no longer be able to automatically remove uploaded files. Only upload on unmetered Wi-Fi /AutoUpload Configure diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 66c9e258b531..b233effbdbb1 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -34,6 +34,14 @@ Paràmetres avançats Permet compartir de nou URL base + Inhabilitació del porta-retalls + Inhabilitació de la introducció + Inhabilitació del registre + Inhabilitació de llocs externs + Inhabilitació de compte múltiple + Inhabilitació de la compartició + Aplicació de la protecció + Nom del servidor intermediari Port del proxy Mostra un giny del tauler Cerca a %s @@ -54,6 +62,7 @@ La tasca s\'ha suprimit correctament No s\'ha pogut obtenir la llista de tasques. Comproveu la vostra connexió a Internet. Suprimeix la tasca + La sortida de la tasca encara no està a punt. No s\'han pogut obtenir els tipus de tasques. Comproveu la connexió a Internet. Assistent Desconegut @@ -93,6 +102,8 @@ S\'ha produït un error en processar la sol·licitud d\'inici de sessió. Torneu-ho a provar més tard. Completeu el procés d\'inici de sessió al navegador manté a la carpeta original, ja que és només de lectura + S\'ha canviat el comportament de la càrrega automàtica + A causa de les noves restriccions imposades per Google, la funció de càrrega automàtica ja no podrà eliminar automàticament els fitxers carregats. Pujada només quan hi hagi Wi-Fi sense límits /Càrrega automàtica Configura @@ -180,7 +191,10 @@ Conflicte de carpetes Fitxer local Si seleccioneu ambdues versions, s\'afegirà un numero al nom del fitxer local. + Si seleccioneu ambdues versions, la carpeta local tindrà un número afegit al nom. Fitxer del servidor + Còpia de seguretat dels contactes + Cal permís dels contactes. Icona d\'usuari per a la llista de contactes No s\'han concedit permisos, no s\'ha importat res Contactes @@ -207,6 +221,7 @@ Nova presentació Nou full de càlcul Afegiu una descripció per a la carpeta + Afegeix la descripció de la carpeta Credencials inhabilitades Còpia de seguretat diària Dades a fer una còpia de seguretat @@ -223,15 +238,21 @@ No s\'han verificat els duplicats. Aquest algorisme de resum no és disponible al vostre telèfon. l\'Inici de sessió via enllaç directe ha fallat! + Inicia la sessió amb %1$s a %2$s Inhabilita Descarta Descarta la notificació Mostra la vostra contrasenya de 12 paraules No molesteu + Múltiples imatges Fitxer PDF + Trieu el tipus d\'exportació + Ha fallat la generació del PDF + S\'està generant el PDF... Fet No esborrar No s\'ha pogut crear el fitxer local + Nom de fitxer no vàlid per al fitxer local Baixa la darrera versió de desenvolupament No s\'ha pogut baixar%1$s La baixada ha fallat, torneu a iniciar la sessió @@ -241,7 +262,10 @@ S\'està descarregant… %1$s descarregat Descarregat + Alguns fitxers s\'han cancel·lat durant la baixada que ha fet l\'usuari + S\'ha produït un error en baixar els fitxers Encara no s\'ha descarregat + S\'ha produït un error inesperat en baixar els fitxers Tanca la barra lateral Comunitat Imatge de fons de la capçalera del calaix @@ -250,9 +274,11 @@ Assistent Preferits Multimèdia + Carpetes de grup Inici Notificacions Al dispositiu + Fitxers personals Modificat recentment Compartit Fitxers suprimits @@ -262,12 +288,19 @@ %1$s de %2$s en ús %1$s en ús Pujada automàtica + El comptador és massa antic + No s\'ha trobat la funció resum E2E encara no està configurat + No és possible sense connexió a Internet + La signatura no coincideix Assistent Més + Notes Converses + Més aplicacions de Nextcloud Notes del Nextcloud Nextcloud Talk + No s\'ha pogut triar l\'adreça electrònica. Activa el xifrat Arranjament de xifrat S\'està desxifrant… @@ -298,7 +331,9 @@ Informa d\'un problema al seguidor? (requereix un compte de GitHub) S\'ha produït un error mentre es recuperava el fitxer S\'ha produït un error recuperant les plantilles + S\'ha produït un error en mostrar el quadre de diàleg de configuració del xifratge S\'ha produït un error iniciant la càmera + S\'ha produït un error en iniciar l\'escaneig del document Comptes S\'ha creat Nom de la tasca diff --git a/app/src/main/res/values-de/ionos-strings.xml b/app/src/main/res/values-de/ionos-strings.xml new file mode 100644 index 000000000000..5cb86d606c01 --- /dev/null +++ b/app/src/main/res/values-de/ionos-strings.xml @@ -0,0 +1,32 @@ + + + + + + + Speicherberechtigungen + Speicherberechtigungen + %1$s braucht die Erlaubnis, auf Ihre Dateien zuzugreifen, um besser zu funktionieren. Sie können den Zugriff erlauben oder verweigern. + %1$s braucht die Erlaubnis, auf Ihre Dateien zuzugreifen, um besser zu funktionieren. Sie können den Zugriff erlauben oder verweigern. + + + Name oder E-Mail-Adresse… + + + Datenschutz-Einstellungen + Diese Anwendung verwendet Cookies und ähnliche Technologien. Durch Klicken auf Zustimmen akzeptieren Sie die Verarbeitung und auch die Weitergabe Ihrer Daten an Dritte. Weitere Informationen, auch zur Datenverarbeitung durch Drittanbieter, finden Sie in den Einstellungen und in unserer Datenschutzhinweise Sie können die Nutzung der Tools Ablehnen oder Ihre Auswahl jederzeit über Ihre Einstellungen anpassen. + Einstellungen + Zustimmen + Zur Optimierung unserer App erfassen wir anonymisierte Daten. Hierzu nutzen wir Softwarelösungen verschiedener Partner. Wir möchten Ihnen volle Transparenz und Entscheidungsgewalt über die Verarbeitung und Erfassung Ihrer anonymisierten Nutzungsdaten geben. Ihre Einstellungen können Sie auch später jederzeit unter dem Menüpunkt Datenschutz ändern. Bitte beachten Sie jedoch, dass die Datenerfassung einen erheblichen Beitrag zur Optimierung dieser App leistet und Sie diese Optimierungen durch die Unterbindung der Datenübermittlung verhindern. + Die Erfassung dieser Daten ist notwendig, um wesentliche Funktionen der App nutzen zu können. + Erforderliche Datenerfassung + Diese Daten helfen uns, die App-Nutzung für Sie zu optimieren und Systemabstürze und Fehler schneller zu identifizieren. + Analyse der Datenerfassung zur bedarfsgerechten Gestaltung + Einstellungen speichern + + \ No newline at end of file diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index d390c35d2d6f..f35eab33c89d 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -103,6 +103,8 @@ Fehler bei der Verarbeitung Ihrer Anmeldeanforderung. Bitte versuchen Sie es später erneut. Bitte schließen Sie den Anmeldevorgang in Ihrem Browser ab im Original-Verzeichnis belassen, da nur lesbar + Automatisches Hochlade-Verhalten geändert + Aufgrund neuer Einschränkungen durch Google kann die Funktion zum automatischen Hochladen Dateien nicht mehr automatisch entfernen. Nur über gebührenfreies WLAN hochladen /AutoUpload Einrichten diff --git a/app/src/main/res/values-es/ionos-strings.xml b/app/src/main/res/values-es/ionos-strings.xml new file mode 100644 index 000000000000..d459b9a3cbb6 --- /dev/null +++ b/app/src/main/res/values-es/ionos-strings.xml @@ -0,0 +1,31 @@ + + + + + + + Permisos de almacenamiento + Permisos de almacenamiento + %1$s necesita permiso para acceder a tus archivos para funcionar mejor. Puedes permitir o denegar el acceso. + %1$s necesita permiso para acceder a tus archivos para funcionar mejor. Puedes permitir o denegar el acceso. + + + Nombre o dirección de correo electrónico… + + + Configuración de la protección de datos + Esta aplicación utiliza cookies y tecnologías similares. Al hacer clic en Aceptar, consientes el procesamiento y la transferencia de tus datos a terceros. Encontrarás más información, incluida la relativa al procesamiento de datos por parte de terceros, en los ajustes y en nuestra política de privacidad Puedes rechazar la utilización de estas herramientas reject o modificar tu selección en cualquier momento a través de tus ajustes. + Ajustes + Aceptar + Para optimizar nuestra aplicación, recogemos datos anónimos. Para ello utilizamos soluciones de software de diferentes socios. Nos gustaría ofrecerte una transparencia y una ayuda en la toma de decisiones total sobre el procesamiento y la recopilación de tus datos de uso anonimizados. Posteriormente, puedes cambiar tu configuración en cualquier momento en el punto de menú Protección de datos. Sin embargo, ten en cuenta que la recopilación de datos contribuye de manera significativa a la optimización de esta aplicación y que si detienes el envío de datos impedirás estas optimizaciones. + La recopilación de estos datos es necesaria para poder utilizar las funciones esenciales de la aplicación. + Recopilación necesaria de datos + Estos datos nos ayudan a optimizar el uso de la aplicación y a identificar los fallos del sistema y los errores más rápidamente. + Análisis de la recopilación de datos para un diseño personalizado basado en las necesidades de cada usuario + Guardar ajustes + \ No newline at end of file diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index 0a164a89cc2b..ae0cdf9d987c 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -35,6 +35,14 @@ Ezarpen aurreratuak Baimendu birpartekatzea Oinarri URLa + Desgaitu arbela + Desgaitu introdukzioa + Desgaitu egunkaria + Desgaitu kanpoko guneak + Desgaitu kontu ugariak + Desgaitu partekatzea + Betearazi babesa + Proxy ostalaria Proxy-ataka Paneleko trepeta bat erakusten du Bilatu %s(e)n @@ -55,6 +63,7 @@ Zeregina ongi ezabatu da Ezin da zereginen zerrenda eskuratu. Egiaztatu Interneteko konexioa. Ezabatu zeregina + Zereginaren irteera ez dago prest oraindik. Ezin dira zeregin motak eskuratu, egiaztatu Interneteko konexioa. Morroia Ezezaguna @@ -94,6 +103,8 @@ Arazo bat egon da zure saioa hasteko eskaera prozesatzean. Mesedez saiatu geroxeago. Mesedez, osatu saioa hasteko prozesua zure nabigatzailean Jatorrizko karpetan mantendu da, soilik irakurtzeko delako + Igoera automatikoaren portaera aldatu da + Googlek ezarritako murrizketa berrien ondorioz, auto-igoera ezaugarriak ezingo ditu igotako fitxategiak automatikoki ezabatu. Kargatu soilik mugarik gabeko Wi-Fi bidez /Auto-karga Konfiguratu @@ -360,6 +371,7 @@ Fitxategi-izena dagoeneko existitzen da. Ezabatu Errorea fitxategiaren jarduerak berreskuratzean + Ez dago eskuragarri kontaktuak hautatzeko aplikaziorik Huts egin du xehetasunak kargatzean Fitxategia Mantendu @@ -387,6 +399,7 @@ Biltegiratze-baimenak %1$shobeto dabil biltegia atzitzeko baimenekin. Fitxategi guztietarako sarbide osoa aukera dezakezu, edo argazki eta bideoak \"irakurtzeko soilik\" baimena eman. %1$s-k fitxategiak kudeatzeko baimenak behar ditu fitxategiak igotzeko. Fitxategi guztietarako sarbide osoa aukera dezakezu, edo \"irakurtzeko soilik\" baimena argazki eta bideoentzat. + Baimendu sarbidea beste aplikaziotatik Helburua egiaztatzen... Garbitzen… Datu-biltegiratze karpeta eguneratzen @@ -397,6 +410,7 @@ Ezin izan da helburuko fitxategia idatzi Migrazioak huts egin du Indizea eguneratzeak huts egin du + %1$s\n(%3$s / %2$s) Datuak lekuz aldatzen... Amaituta Ordeztu @@ -592,6 +606,7 @@ Jakinarazpenik ez Begiratu beranduago, mesedez. Eragiketaren zain + Kentze eragiketaren zain Internet konexiorik ez Interneteko konexiorik gabe ere, zure karpetak antola ditzakezu, fitxategiak sortu. Konektatuta berriro zaudenean, zain dauden ekintzak automatikoki sinkronizatuko dira. Lineaz kanpo zaude, baina lanak jarraitzen du @@ -654,6 +669,8 @@ Sinkronizatu Zure egutegiaren eta kontaktuen eguneroko babeskopia Zure kontaktuen eguneroko babeskopia + Datu biltegiaren kokapena + Kudeatu data biltegiaren kokapena Ustekabeko errorea DAVx5 konfiguratzean (lehen DAVdroid izenez ezagutzen zena) Muturretik muturrerako enkriptatzea konfiguratuta dago! E2E mnemoteknikoa @@ -694,6 +711,7 @@ Gaia Tarteak Kudeatu barruko karpetak bi norabideko sinkronizaziorako + Gaitu bi norabideko sinkronizazioa Iluna Argia Sistemaren berdina @@ -734,6 +752,7 @@ Eskatu kontuaren ezabaketa Ezabatzeko eskaera Eskatu kontua behin betiko ezabatzea zerbitzu-hornitzailetik + Politikak edo baimenek ez dute berriro partekatzen uzten Ez dago tamainaz aldatutako irudirik eskuragarri. Irudi osoa deskargatu nahi duzu? Berreskuratu fitxategia Berrezarri babeskopia @@ -802,6 +821,7 @@ Partekatze baimenak %1$s (urrunekoa) %1$s (elkarrizketa) + Izena, federatutako hodei IDa edo helbide elektronikoa... Bidali posta-mezu berria Oharra hartzailearentzat Ezarpenak @@ -920,6 +940,9 @@ Ezabatu betirako Zakarrontzia kargatzeak huts egin du! Fitxategiak ezin dira betirako ezabatu! + Desgaitu karpeta guztientzat + Bi norabidetan sinkronizatzeko karpeta bat jartzeko, mesedez, gaitu karpetaren xehetasunen fitxan. + Ez da bi norabideko sinkronizazioa ezarri Barneko bi norabideko sinkronizazioa Ustekabeko errore bat gertatu da Gertaera ez da aurkitu, beti sinkroniza dezakezu eguneratzeko. Webera birbideratzen… @@ -929,6 +952,7 @@ Irakurri gabeko iruzkinak daude Kendu enkriptatzea Kendu gogokoetatik + Barneko bi norabideko sinkronizazio karpeta kendu Errorea gertatu da fitxategi edo karpeta hau partekatzeari uzten saiatzerakoan. Ezin izan da partekatzeari utzi. Mesedez egiaztatu fitxategia existitzen dela. fitxategiaren konpartitzea kentzeko @@ -1047,6 +1071,7 @@ Itxaron momentu bat… Gordetako nortasun-datuak konprobatzen Fitxategia biltegiratze pribatutik kopiatzen + Luzapena aldatzeak fitxategi hau beste aplikazio batean irekitzea eragin dezake Eguneratu Android System WebView aplikazioa saioa hasteko Eguneratu Eguneratu Android System WebView @@ -1054,11 +1079,20 @@ Salto egin Berria %1$s-n Zein da zure egoera? + Widgetak %1$s 25 edo geroago bakarrik daude eskuragarri Panelaren aplikazioa gaitzen denean Ez dago erabilgarri Fitxategiak deskargatzen… Bidali mezu elektronikoa Datu-biltegiratze karpeta ez dago! Hau babeskopia eta leheneratzea beste gailu batean egiteagatik izan daiteke. Itzuli balio lehenetsietara. Egiaztatu ezarpenak datu biltegiratze-karpeta doitzeko. + + ordu %d + %d ordu + + + minutu %d + %d minutu + Ezin izan da %1$d fitxategia sinkronizatu (gatazkak: %2$d) Ezin izan dira %1$d fitxategi sinkronizatu (gatazkak: %2$d) diff --git a/app/src/main/res/values-fr/ionos-strings.xml b/app/src/main/res/values-fr/ionos-strings.xml new file mode 100644 index 000000000000..5c175e5172c8 --- /dev/null +++ b/app/src/main/res/values-fr/ionos-strings.xml @@ -0,0 +1,32 @@ + + + + + + + Autorisations de stockage + Autorisations de stockage + %1$s a besoin d\'une autorisation pour accéder à vos fichiers. Vous pouvez autoriser ou refuser l\'accès. + %1$s a besoin d\'une autorisation pour accéder à vos fichiers. Vous pouvez autoriser ou refuser l\'accès. + + + Nom ou adresse e-mail… + + + Paramètres de confidentialité + Cette application utilise des cookies et des technologies similaires. En cliquant sur Accepter, vous acceptez le traitement et le transfert de vos données à des tiers. Vous trouverez plus d’informations, notamment sur le traitement des données par des tiers, dans les paramètres et dans notre Politique de confidentialité Vous pouvez refuser d\'utiliser les outils Refuser ou modifier votre sélection à tout moment via vos paramètres. + Paramétrages + Accepter + Pour optimiser notre application, nous recueillons des données anonymes. Pour ce faire, nous utilisons les solutions logicielles de différents partenaires. Nous souhaitons vous offrir une transparence et un pouvoir de décision entiers sur le traitement et la collecte de vos données d\'utilisation anonymes. Vous pouvez également modifier vos paramètres à tout moment par la suite dans le menu Protection des données. Veuillez toutefois noter que la collecte de données contribue de manière significative à l\'optimisation de cette application et que vous empêchez ces optimisations en arrêtant le transfert de données. + La collecte de ces données est nécessaire afin d\'utiliser les fonctions essentielles de l\'application. + Collecte de données nécessaire + Ces données nous aident à optimiser l\'utilisation de l\'application et à identifier plus rapidement les pannes du système et les erreurs. + Analyse de la collecte de données pour une conception basée sur les besoins + Sauvegarder les paramètres + + \ No newline at end of file diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index a8a3707f2e22..20021336b169 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -103,6 +103,8 @@ Un problème est survenu lors du traitement de votre demande de connexion. Veuillez réessayer plus tard. Veuillez finaliser la connexion dans votre navigateur conservé dans le dossier original (puisqu\'il est en lecture seule) + Le comportement du téléversement automatique a changé + À cause de nouvelles restrictions imposées par Google, la fonctionnalité de téléversement automatique ne pourra plus automatiquement supprimer les fichiers téléversés. Téléverser par Wi-Fi uniquement /TéléversementAuto Configurer diff --git a/app/src/main/res/values-ga/strings.xml b/app/src/main/res/values-ga/strings.xml index 87f0265c9d37..c2ea1c299233 100644 --- a/app/src/main/res/values-ga/strings.xml +++ b/app/src/main/res/values-ga/strings.xml @@ -103,6 +103,8 @@ Bhí fadhb ann d\'iarratas logáil isteach a phróiseáil. Bain triail eile as ar ball le do thoil. Críochnaigh an próiseas logáil isteach i do bhrabhsálaí le do thoil coinnithe sa bhunfhillteán, mar atá sé inléite amháin + Athraíodh iompar uaslódála uathoibríoch + De bharr srianta nua arna bhforchur ag Google, ní bheidh an ghné uaslódála uathoibríoch in ann comhaid uaslódáilte a bhaint go huathoibríoch a thuilleadh. Uaslódáil ar Wi-Fi neamh-mhéadraithe amháin /Uaslódáil Uathoibríoch Cumraigh diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 95b7ced214f8..67f1549dfbf8 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -103,6 +103,8 @@ Houbo un problema ao procesar a súa solicitude de acceso. Ténteo de novo máis tarde. Complete o proceso de acceso no seu navegador mantense no cartafol orixinal, xa que é de só lectura + O comportamento do envío automático cambiou + Por mor dás novas restricións impostas por Google, a función de envío automático xa non poderá retirar automaticamente os ficheiros enviados. Enviar só con wifi sen límite de datos /EnvíoAutomático Configurar diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index 10c45e18440e..3ec6517f4590 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -103,6 +103,8 @@ C\'è stato un problema nella richiesta di login. Per favore, riprova più tardi Completa il procedimento di login nel tuo browser lasciato nella cartella originale, poiché è in sola lettura + Il comportamento del caricamento automatico è stato modificato + A causa delle nuove restrizioni imposte da Google, la funzionalità di caricamento automatico non sarà più in grado di rimuovere automaticamente i file caricati. Carica solo su Wi-Fi senza limitazioni /AutoUpload Configura diff --git a/app/src/main/res/values-land/ionos-integers.xml b/app/src/main/res/values-land/ionos-integers.xml new file mode 100644 index 000000000000..69325648f8cb --- /dev/null +++ b/app/src/main/res/values-land/ionos-integers.xml @@ -0,0 +1,11 @@ + + + + + 4 + diff --git a/app/src/main/res/values-large-land/ionos-integers.xml b/app/src/main/res/values-large-land/ionos-integers.xml new file mode 100644 index 000000000000..69325648f8cb --- /dev/null +++ b/app/src/main/res/values-large-land/ionos-integers.xml @@ -0,0 +1,11 @@ + + + + + 4 + diff --git a/app/src/main/res/values-large/ionos-dimens.xml b/app/src/main/res/values-large/ionos-dimens.xml new file mode 100644 index 000000000000..364b222d48a8 --- /dev/null +++ b/app/src/main/res/values-large/ionos-dimens.xml @@ -0,0 +1,29 @@ + + + + + + + 36dp + + + 48dp + 128dp + 35sp + 49sp + 32dp + 18sp + 24sp + 24dp + 24dp + 480dp + 16dp + 288dp + 12dp + + \ No newline at end of file diff --git a/app/src/main/res/values-large/ionos-integers.xml b/app/src/main/res/values-large/ionos-integers.xml new file mode 100644 index 000000000000..48b14899437c --- /dev/null +++ b/app/src/main/res/values-large/ionos-integers.xml @@ -0,0 +1,11 @@ + + + + + 3 + diff --git a/app/src/main/res/values-night/ionos-colors.xml b/app/src/main/res/values-night/ionos-colors.xml new file mode 100644 index 000000000000..9374b4c4e911 --- /dev/null +++ b/app/src/main/res/values-night/ionos-colors.xml @@ -0,0 +1,19 @@ + + + @color/bg_default + + @color/white + @color/primary + @color/primary_dark + @color/disabled_text + + @color/primary + @color/white + + @color/primary + diff --git a/app/src/main/res/values-night/ionos-scanbot-theme.xml b/app/src/main/res/values-night/ionos-scanbot-theme.xml new file mode 100644 index 000000000000..709c3f4e2d95 --- /dev/null +++ b/app/src/main/res/values-night/ionos-scanbot-theme.xml @@ -0,0 +1,47 @@ + + + + + + diff --git a/app/src/main/res/values-nl/ionos-strings.xml b/app/src/main/res/values-nl/ionos-strings.xml new file mode 100644 index 000000000000..f65880dd2c22 --- /dev/null +++ b/app/src/main/res/values-nl/ionos-strings.xml @@ -0,0 +1,32 @@ + + + + + + + Opslag-toestemmingen + Opslag-toestemmingen + %1$s vereist toestemming voor toegang tot je bestanden voor een betere functionaliteit. Je kunt de toegang toestaan of weigeren. + %1$s vereist toestemming voor toegang tot je bestanden voor een betere functionaliteit. Je kunt de toegang toestaan of weigeren. + + + Naam of e-mailadres… + + + Privacyinstellingen + Deze toepassing gebruikt cookies en vergelijkbare technologie. Door akkoord te gaan, accepteer je de verwerking en de overdracht van je gegevens aan derden. privacybeleid . Je kunt het gebruik van de tools op elk moment weigeren of je keuze aanpassen via je instellingen. + Instellingen + Akkoord + Om onze app te optimaliseren, verzamelen we anonieme gegevens. Hiervoor gebruiken we softwareoplossingen van verschillende partners. Wij willen je volledige transparantie en beslissingsbevoegdheid geven over de verwerking en verzameling van je geanonimiseerde gebruiksgegevens. Je kunt je instellingen altijd wijzigen via het menu \'Privacy\'. Houd er rekening mee dat het verzamelen van gegevens een aanzienlijke bijdrage levert aan de optimalisatie van deze app. Als je de dataverzameling uitschakelt, zijn deze optimalisaties beperkt of niet meer mogelijk. + Het verzamelen van deze gegevens is noodzakelijk om essentiële functies van de app te kunnen gebruiken. + Noodzakelijke dataverzameling + Deze gegevens helpen ons om het gebruik van de app voor je te optimaliseren en om systeemcrashes en fouten sneller te identificeren. + Dataverzameling voor gebruikersgebaseerde app-optimalisatie + Instellingen opslaan + + \ No newline at end of file diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 879ae2b8e68a..19b6a387b4c5 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -30,10 +30,19 @@ Activiteit Nog een link toevoegen Voeg een nieuwe openbare deellink toe + Beveiligde bestands-drop Voeg toe aan %1$s Geavanceerde instellingen Opnieuw delen toestaan Basis URL + Klembord uitschakelen + Intro uitschakelen + Log uitschakelen + Externe sites uitschakelen + Multi-account uitschakelen + Delen uitschakelen + Beveiliging afdwingen + Proxy hostnaam Proxy poort Toont één widget van dashboard Zoeken in %s @@ -93,6 +102,7 @@ Er was een probleem met het verwerken van je aanmelding. Probeer het later nog eens. Vervolg de aanmelding in je browser bewaard worden in originele map, omdat die Alleen-lezen is + Auto-upload gedrag gewijzigd Upload alleen met Wi-Fi /AutomatischUploaden Configureren @@ -177,8 +187,10 @@ Alleen lokaal Conflictoplossingsvenster kan niet geladen worden Conflicterend bestand%1$s + Mapconflict Lokaal bestand Als je beide versies selecteert, zal het lokale bestand een nummer aan de naam toegevoegd krijgen. + Als je beide versies selecteert, zal het lokale bestand een nummer aan de naam toegevoegd krijgen. Serverbestand Contacten Backup Toestemming voor contact vereist @@ -275,8 +287,11 @@ %1$s van %2$s gebruikt %1$s gebruikt Automatisch uploaden + Teller is te oud + Hash niet gevonden E2E is nog niet ingesteld Niet mogelijk zonder internetverbinding + Handtekening komt niet overeen Assistent Meer Notities @@ -354,6 +369,7 @@ Bestandsnaam bestaat al Verwijderen Fout bij ophalen activiteiten van bestand + Geen app beschikbaar voor het selecteren van contactpersonen Kon details niet laden Bestand Behouden @@ -381,6 +397,7 @@ Opslagmachtigingen %1$s werkt het best met opslagmachtigingen. Je kan kiezen tussen volledige toegang tot alle bestanden, of alleen lezen toegang tot foto\'s en video\'s. %1$s heeft bestandsbeheer machtiging nodig om bestanden te uploaden. Je kan kiezen tussel volledige toegang tot alle bestanden, of alleen lezen toegang tot foto\'s en videos. + Toegang door andere apps toestaan Doelmap controleren… Opruimen… Bijwerken gegevensopslagmap @@ -391,6 +408,7 @@ Kon niet schrijven naar bestand Fout tijdens migratie Index bijwerken mislukt + %1$s\n(%2$s / %3$s) Gegevens verplaatsen… Afgerond Vervangen @@ -402,6 +420,13 @@ Index bijwerken… Gebruiken Wachten op volledige synchronisaties… + Huidige mapnaam is ongeldig, hernoem map a.u.b. Terug... + Mapnaam bevat gereserveerde namen of ongeldige tekens + %s is een ongeldige bestandsextensie + Bestandsnamen mogen geen spaties aan het begin of eind bevatten + Naam bevat ongeldige tekens %s + %s is een ongeldige naam + %s. Hernoem het bestand voor verplaatsen of kopiëren Bestand niet gevonden Synchroniseren van het gewenste bestand mislukt. Laatst beschikbare versie wordt weergegeven. Hernoemen diff --git a/app/src/main/res/values-sk-rSK/strings.xml b/app/src/main/res/values-sk-rSK/strings.xml index 24ddd02ed512..26132f0f3e7a 100644 --- a/app/src/main/res/values-sk-rSK/strings.xml +++ b/app/src/main/res/values-sk-rSK/strings.xml @@ -35,6 +35,14 @@ Rozšírené nastavenia Povoliť zdieľanie ďalej Základná URL + Zakázať Schránku + Zakázať intro + Zakázať logovanie + Zakázať Externé stránky + Zakázať Multi-účty + Zakázať zdieľanie + Vynútiť Ochranu + Proxy Hostname Brána proxy Zobrazí jeden widget z hlavného panela Hľadať v %s @@ -91,8 +99,11 @@ %1$s nepodporuje viacero účtov Nepodarilo sa nadviazať spojenie Zrušiť prihlasovanie + Pri spracovaní vašej žiadosti o prihlásenie sa vyskytol problém. Skúste to znova neskôr. Prosím, dokončite proces prihlasovania vo vašom prehliadači ponechané v pôvodnom priečinku, pretože je iba na čítanie + Správanie automatického nahrávania sa zmenilo + Z dôvodu nových obmedzení uložených spoločnosťou Google už funkcia automatického nahrávania nebude môcť automaticky odstraňovať nahrané súbory. Nahrávaj iba na neobmedzenom Wi-Fi /AutomatickéNahrávanie Nastaviť @@ -180,6 +191,7 @@ Konflikt adresárov Lokálny súbor Ak vyberiete obe verzie, miestny súbor bude mať k svojmu názvu pridané číslo. + Ak vyberiete obe verzie, lokálny priečinok bude mať k názvu pripojené číslo. Súbor na serveri Záloha kontaktov Vyžaduje sa prístup ku kontaktom @@ -208,6 +220,8 @@ Nový priečinok Nová prezentácia Nová tabuľka + Pridať popis adresára + Pridá popis adresára Prihlasovacie údaje zakázané Denná záloha Dáta pre zálohovanie @@ -274,6 +288,7 @@ Využité: %1$s z %2$s %1$s použitých Automatické nahratie + Počítadlo je veľmi staré Hash nebol nájdený E2E zatiaľ nie je nastavené Nie je možné bez internetového pripojenia @@ -351,9 +366,11 @@ Nepodarilo sa aktualizovať UI Pridať do obľúbených Obľúbené + Zdieľaný súbor nemôže byť aktualizovaný Súbor už existuje Zmazať Chyba pri načítavaní aktivít pre súbor + Nie je k dispozícii žiadna aplikácia pre výber kontaktov Nepodarilo sa načítať podrobnosti Súbor Ponechať @@ -364,6 +381,7 @@ Nie sú tu žiadne súbory Žiadne výsledky v tomto priečinku Žiadne výsledky + Vášmu vyhľadávaniu nezodpovedá žiadny súbor ani adresár Nič tu nie je. Môžete pridať priečinok. Tu sa zobrazia nahraté súbory a priečinky V posledných 7 dňoch sa nenašli žiadne zmeny @@ -380,6 +398,7 @@ Oprávnenia úložiska %1$s funguje najlepšie s oprávnením pre prístup k úložisku. Môžete si vybrať úplný prístup ku všetkým súborom alebo prístup iba na čítanie k fotografiám a videám. %1$s potrebuje na nahrávanie súborov oprávnenie na správu súborov. Môžete si vybrať úplný prístup ku všetkým súborom alebo prístup iba na čítanie k fotografiám a videám. + Povoliť prístup z ostatných aplikácií Kontrolujem umiestnenie… Čistenie… Aktualizujem priečinok dátového úložiska @@ -390,6 +409,7 @@ Nepodarilo sa zapísať do cieľového súboru Chyba počas migrácie Nepodarilo sa aktualizovať index + %1$s\n(%2$s / %3$s) Presúvam dáta… Hotovo Nahradiť @@ -401,9 +421,18 @@ Aktualizujem zoznam… Použi Čakám na kompletnú synchronizáciu… + Aktuálny názov adresára je neplatný, premenujte priečinok. Prebieha presmerovanie domov… + Cesta k adresáru obsahuje vyhradené názvy alebo neplatné znaky + %s nie je povolená prípona súboru + Súbory nesmú obsahovať medzeru na začiatku alebo konci názvu + Názov obsahuje nepovolené znaky: %s + %s nie je povolený názov + %s. Prosím premenujte súbor predtým ako bude skopírovaný alebo presunutý + Niektorý obsah nie je možné nahrať, pretože obsahuje vyhradené mená alebo neplatné znaky Súbor nenájdený Súbor sa nepodarilo zosynchronizovať. Zobrazuje sa posledná dostupná verzia. Premenovať + Rovnaký súbor už existuje, nezistil sa žiadny konflikt Chyba pri obnove verzie súboru! Verzia súboru úspešne obnovená. Podrobnosti @@ -423,6 +452,7 @@ Priečinok už existuje Vytvoriť Nie sú tu žiadne priečinky + Názov adresára nemôže byť prázdny Vybrať Vyberte cieľový priečinok Kopírovať @@ -470,6 +500,8 @@ Nahrať iba počas nabíjania /InstantUpload Interná obojsmerná synchronizácia + Zatiaľ nie, čoskoro bude zosynchronizované + Pre nastavenie šifrovaného adresára je potrebné internetové pripojenie Neplatná URL Neviditeľné Štítok nemôže byť prázdny @@ -508,6 +540,7 @@ Vyčistiť dáta Nastavenie, databázové a servrové certifikáty z dát %1$sbudú trvalo vymazané.\n\nStiahnuté súbory budú zachované.\n\nTento proces môže chvíľu trvať. Spravovať miesto + Dosiahli ste maximálny limit počtu súborov pre nahrávanie. Prosím nahrajte menej ako 500 súborov naraz. Multimediálny súbor sa nedá streamovať Nepodarilo sa prečítať súbor médií Súbor médií má neplatné kódovanie @@ -553,6 +586,8 @@ Nebolo možné odoslať poznámku Ikona poznámky Nepodarilo sa vykonať akciu. + Zobraziť upozornenia s výsledkami operácií na pozadí + Operácie na pozadí Zobrazuje postup sťahovania Stiahnuté Zobrazuje progres a výsledok synchronizácie súborov @@ -566,10 +601,20 @@ Zobrazuje postup nahrávania Nahrané Ikona notifikácie + Máte neprečítané upozornenia Žiadne upozornenia Prosím skontrolujte znova neskôr. Čakajúca operácia + Pribieha operácia mazania Bez internetového pripojenia + Dokonca aj bez internetového pripojenia môžete organizovať svoje adresáre a vytvárať súbory. Keď budete opäť online, vaše nespracované akcie sa automaticky zosynchronizujú. + Nie ste pripojený, ale môžete pokračovať v práci + Žiadny súbor zatiaľ neexistuje. Prosím, nahrajte prvý súbor. + Konfliktný adresár? %s + Zmazať Offline Adresár + Offline operácia nemôže byť dokončená. %s + Offline operace + Spúšťanie Offline operácií 1 hodina Pripojené Stav pripojenia @@ -623,6 +668,8 @@ Synchronizovať Denná záloha vašeho kalendára a kontaktov Denná záloha vašich kontaktov + Umiestnenie úložiska údajov + Spravovať umiestnenie úložiska údajov Neočakávaná chyba pri nastavovaní DAVx5 (predtým známy ako DAVdroid) End-to-end šifrovanie je nastavené! E2E mnemotechnické @@ -656,12 +703,13 @@ Návrhy Nextcloud aplikácií v navigačnom záhlaví Zobraziť skryté súbory Získajte zdrojový kód - Priečinok dátového úložiska - Spravovať priečinky pre automatické nahrávanie + Adresár dátového úložiska + Spravovať adresáre pre automatické nahrávanie Lokálny priečinok Vzdialený priečinok Motív vzhľadu Interval + Povoliť obojstrannú synchronizáciu Tmavý Svetlý Nasledovať systém @@ -677,6 +725,7 @@ Žiadne push notifikácie z dôvodu zastaraného prihlásenia. Prosím skúste znova pridať váš účet. Push notifikácie sú v súčasnosti nedostupné. QR kód nie je možné načítať! + Nepodarilo sa nájisť súbor pre nahratie Vyskúšajte %1$s na vašom zariadení! Chcel by som odporučiť %1$s pre vaše zariadenie.\nZískať ho môžete aj tu: %2$s %1$s alebo %2$s @@ -722,8 +771,9 @@ Synchronizácia s DAVx5 Chyba pri získavaní výsledkov vyhľadávania Zabezpečené zdieľanie nie je nastavené pre tohto užívateľa. + Bezpečné zdieľanie… Vybrať všetko - Nastaviť priečinok médií + Nastaviť adresár médií Vyberte jednu šablónu Vybrať šablónu Odoslať @@ -744,11 +794,11 @@ %1$s (skupina) Sprístupniť interný odkaz Interný odkaz na zdieľanie funguje iba pre používateľov s prístupom k tomuto súboru - Interný odkaz na zdieľanie funguje iba pre používateľov s prístupom k tomuto priečinku + Interný odkaz na zdieľanie funguje iba pre užívateľov s prístupom k tomuto adresáru na %1$s Sprístupniť odkaz Musíte vložiť heslo - Pri pokuse o zdieľanie tohto súboru alebo priečinka došlo k chybe. + Pri pokuse o zdieľanie tohto súboru alebo adresára došlo k chybe. Zdieľanie nie je možné. Skontrolujte, či súbor existuje. sprístupniť tento súbor Zadajte voliteľné heslo @@ -849,7 +899,7 @@ Synchronizácia neúspešná Synchronizácia neúspešná, je potrebné sa znovu prihlásiť Obsah súboru je zosynchronizovaný - Synchronizáciu priečinka %1$s sa nedarí dokončiť + Synchronizáciu adresára %1$s sa nedarí dokončiť Od verzie 1.3.16 sú súbory nahrané z tohoto zariadenia kopírované do lokálneho priečinka %1$s, aby sa zabránilo strate dát pri synchronizácii jedného súboru s viacerými účtami.\n\nVšetky súbory nahraté predchádzajúcimi verziami aplikácie boli z tohoto dôvodu prekopírované do priečinka %2$s. Bohužiaľ sa objavila chyba zabraňujúca dokončeniu tejto operácie v priebehu synchronizácie účtu. Buď môžete súbor(y) ponechať ako sú a odobrať odkaz z priečinka %3$s, alebo presunúť súbor(y) do priečinka %1$s a zachovať odkaz na %4$s.\n\nNižšie je uvedený lokálny súbor(y) a vzdialený súbor(y) v %5$s, s ktorým je prepojený. Niektoré lokálne súbory boli zabudnuté Načítavam najnovšiu verziu súboru. @@ -862,7 +912,7 @@ Tlačidlo nastavenia Nastavenie priečinkov Okamžité nahrávanie bolo kompletne prepracované. Zmente si nastavenie automatického nahrávania cez hlavné menu.\n\nUžite si nové a rozšírene možnosti automatického nahrávania. - Nenašli sa žiadne priečinky s médiami + Nenašli sa žiadne adresáre s médiami Pre %1$s Typ Zosynchronizované @@ -910,6 +960,7 @@ Nahrať z… Nahrať obsah z iných aplikácií Fotka + Chcete urobiť fotografiu alebo video? Nahrať z fotoaparátu Video Názov súboru diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 40a9221f2360..e89f8d589d9b 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -35,6 +35,14 @@ Напредне поставке Дозволи дељење даље Базни URL + Искључи клипборд + Искључи интро + Искључи дневник + Искључи спољашње сајтове + Искључи вишеструке налоге + Искључи дељење + Форсирај заштиту + Хост име проксија Прокси порт Приказује један виџет са контролне табле Тражи у %s @@ -55,6 +63,7 @@ Задатак је успешно завршен Не може да се преузме листа задатака, молимо вас да проверите везу са интернетом. Обриши задатак + Излаз задатка још увек није спреман. Не могу да се преузму типови задатака, молимо вас да проверите везу са интернетом. Асистент Непознато @@ -94,6 +103,8 @@ Дошло је до проблема при обради вашег захтева за пријаву. Молимо вас да касније покушате поново. Молимо вас да довршите процес пријаве у интернет прегледачу остављен у оригиналној фасцикли јер је само за читање + Промењено је понашање аутоматског отпремања + Услед нових ограничења које је поставио Google, функционалност аутоматског отпремања више неће моћи аутоматски да отпрема фајлове. Отпремај само на бежичним мрежама без ограничења /Аутоматска отпремања Подеси @@ -360,6 +371,7 @@ Име фајла већ постоји Обриши Грешка при добављању активности за фајл + Нема апликације којом могу да се изаберу контакти Грешка при учитавању детаља Фајл Задржи @@ -387,6 +399,7 @@ Дозволе за складиште %1$s најбоље ради када има дозволу да приступи складишту. Можете избрати пуни приступ свим фајловима, или приступ само-за -читање фотографија и видео снимака. %1$s захтева дозволе управљања за отпремање фајлова. Можете изабрати пуни приступ свим фајловима, или приступ само-за-читање слика и видео снимака. + Дозволи приступ из осталих апликација Проверавам одредиште… Чистим… Ажурирам фасциклу са подацима @@ -397,6 +410,7 @@ Не могу да пишем у одредишни фајл Грешка приликом пресељења Грешка при ажурирању индекса + %1$s\n(%2$s / %3$s) Премештам податке… Завршено Замени @@ -592,6 +606,7 @@ Нема обавештења Проверите касније. Операције на чекању + Операција уклањања на чекању Нема везе са интернетом Чак и без везе са интернетом можете да организујете фолдере, креирате фајлове. Када поново будете на мрежи, ваше акције на чекању ће се аутоматски синхронизовати. Ван мреже сте, али се рад наставља @@ -654,6 +669,8 @@ Синхронизуј Дневна резервна копија вашег календара и контаката Дневно архивирање ваших контаката + Локација складишта података + Управљање локацијом складишта података Неочекивана грешка приликом подешавања DAVx5 (раније познатог под именом DAVdroid) Подешено је шифрирање од почетка-до-краја! Е2Е мнемоник @@ -760,6 +777,7 @@ Синхронизација помоћу DAVx5 Грешка приликом добијања резултата претраге За овог корисника није подешено безбедно дељење + Безбедно дељење Означи све Поставите фолдер са медијима Молимо вас да изаберете један шаблон @@ -803,6 +821,7 @@ Дозволе за дељење %1$s(удаљено) %1$s (разговор) + Име, ИД здруженог облака или адреса е-поште… Пошаљи нови и-мејл Напомена примаоцу Поставке @@ -1052,6 +1071,7 @@ Сачекајте мало… Проверавам сачуване акредитиве Копирам фајл из личног складишта + Ако се промена екстензија, фајл ће се можда отварати у другој апликацији Да бисте се пријавили, молимо вас да ажурирате Андроид апликацију System WebView Ажурирај Ажурирај Андроид System WebView diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index afd790fe5a8b..8299cc8578b5 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -63,6 +63,7 @@ Uppgiften har raderats Det går inte att hämta uppgiftslistan, kontrollera din internetanslutning. Ta bort uppgift + Uppgiftens resultat är inte redo än. Det går inte att hämta uppgiftstyper, kontrollera din internetanslutning. Assistent Okänd @@ -102,6 +103,8 @@ Det uppstod ett problem när din inloggningsförfrågan behandlades. Försök igen senare. Slutför inloggningsprocessen i din webbläsare behålls i originalmappen, eftersom den är skrivskyddad + Beteendet för automatisk uppladdning har ändrats + På grund av nya begränsningar från Google kommer funktionen för automatisk uppladdning inte längre att kunna ta bort uppladdade filer automatiskt. Ladda bara upp via obelastad Wi-Fi /AutomatiskUppladdning Konfigurera diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 151101bce501..f6ba502de832 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -103,6 +103,8 @@ Помилка під час обробки запиту на вхід. Спробуйте пізніше. Завершити авторизацію у бравзері залишено у вихідному каталозі, оскільки він доступний лише для читання + Режим автоматичного завантаження змінено + Через нові обмеження, які було запроваджено Google, в процесі автоматичного завантаження завантажені файли неможливо буде вилучити. Завантаження тільки через WiFi /AutoUpload Налаштування diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 47bc7f27c4a6..30efb9373df6 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -41,6 +41,7 @@ 禁用外部网站 禁用多账号 禁用共享 + 强制保护 代理主机名称 代理端口 显示仪表盘中的一个小部件 @@ -102,6 +103,8 @@ 处理你的登录请求时出现问题。请稍后再试。 请在浏览器中完成登录流程 保持为原始的文件夹,即使它是只读的 + 自动上传行为已更改 + 由于谷歌施加的新限制,自动上传功能将不再能够自动删除已上传的文件。 仅通过无流量限制的 Wi-Fi 上传 /自动上传 配置 @@ -644,7 +647,7 @@ 切换 请选择服务器… 禁用节能检查可能会导致在低电池电量状态下仍然上传文件! - 已删除 + 被删除 保留在原始文件夹 移动到应用文件夹 如果文件已经存在怎么办? @@ -954,7 +957,7 @@ Nextcloud 是一个私有文件同步、共享和通信服务器。它是自由 今天 回收站 没有被删除的文件 - 您可以在此处恢复已删除的文件。 + 你可以在此处还原已删除的文件。 文件 %1$s 无法被删除! 文件 %1$s 无法被恢复! 彻底删除 @@ -1091,6 +1094,7 @@ Nextcloud 是一个私有文件同步、共享和通信服务器。它是自由 请稍等… 正在检查保存的证书 正在从私有存储中复制文件 + 修改扩展名可能会导致此文件被不同的应用打开 请更新 Android System WebView 应用程序以进行登录 更新 更新 Android System WebView diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 4ac8b09198a2..a3ccd239a324 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -103,6 +103,8 @@ 處理您的登入請求時出現問題。請稍後再試。 請在瀏覽器中完成登入流程 以唯讀模式保留在原本的資料夾 + 自動上傳行為已更改 + 由於 Google 新施加的限制,自動上傳功能將不再能自動刪除已上傳的檔案。 僅在未計量的Wi-Fi上傳 自動上傳 設定 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 2f922a4a51f7..c85a54adb4ce 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -103,6 +103,8 @@ 處理您的登入請求時出現問題。請稍後再試。 請在瀏覽器中完成登入流程 以唯讀模式保留在原本的資料夾 + 自動上傳行為已變更 + 由於 Google 施加的新限制,自動上傳功能將無法再自動移除已上傳的檔案。 只在非計量收費的 Wi-Fi 上傳 /AutoUpload 設定 diff --git a/app/src/main/res/values/ionos-attrs.xml b/app/src/main/res/values/ionos-attrs.xml new file mode 100644 index 000000000000..009589e48d7f --- /dev/null +++ b/app/src/main/res/values/ionos-attrs.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/ionos-colors-palette.xml b/app/src/main/res/values/ionos-colors-palette.xml new file mode 100644 index 000000000000..a2bb6f620d3d --- /dev/null +++ b/app/src/main/res/values/ionos-colors-palette.xml @@ -0,0 +1,34 @@ + + + + + #0B2A63 + #003d8f + #02102B + #001B41 + #095BB1 + #465A75 + #DBEDF8 + #718095 + #1D2D42 + #F4F7FA + #33F4F7FA + #1474C4 + #C36B00 + #FFAA00 + #2E4360 + #97A3B4 + #DBE2E8 + #F2F5F8 + #95CAEB + #BCC8D4 + #0C8A44 + #BDCDD7 + #3196D6 + #80000000 + \ No newline at end of file diff --git a/app/src/main/res/values/ionos-colors.xml b/app/src/main/res/values/ionos-colors.xml new file mode 100644 index 000000000000..75e0d44a1c2f --- /dev/null +++ b/app/src/main/res/values/ionos-colors.xml @@ -0,0 +1,43 @@ + + + #3672be + #d62e2b + + @color/bg_default + + @color/white + @color/primary + @color/primary_dark + @color/disabled_text + + @color/primary + @color/black + + @color/bg_default + ?android:textColorPrimary + + @color/primary + @color/background_color_inverse + @color/color_transparent + @color/black + @color/black + @color/grey_900 + @color/primary_button_text_color + + @color/text_color + @color/primary + @color/primary + @color/disabled_text + @color/primary + @color/primary + + @color/primary + @color/primary + + @color/bg_default + diff --git a/app/src/main/res/values/ionos-dimens.xml b/app/src/main/res/values/ionos-dimens.xml new file mode 100644 index 000000000000..a2fedff268e9 --- /dev/null +++ b/app/src/main/res/values/ionos-dimens.xml @@ -0,0 +1,34 @@ + + + + + + + 8dp + + + 16dp + 64dp + 18sp + 24sp + 32dp + 14sp + 19sp + 12dp + 24dp + 288dp + 16dp + 288dp + 12dp + + 1dp + 8dp + 14sp + 16dp + + \ No newline at end of file diff --git a/app/src/main/res/values/ionos-integers.xml b/app/src/main/res/values/ionos-integers.xml new file mode 100644 index 000000000000..4529d1a3c362 --- /dev/null +++ b/app/src/main/res/values/ionos-integers.xml @@ -0,0 +1,11 @@ + + + + + 2 + diff --git a/app/src/main/res/values/ionos-scanbot-theme.xml b/app/src/main/res/values/ionos-scanbot-theme.xml new file mode 100644 index 000000000000..3865b99fbe96 --- /dev/null +++ b/app/src/main/res/values/ionos-scanbot-theme.xml @@ -0,0 +1,47 @@ + + + + + + diff --git a/app/src/main/res/values/ionos-strings.xml b/app/src/main/res/values/ionos-strings.xml new file mode 100644 index 000000000000..496284a3c283 --- /dev/null +++ b/app/src/main/res/values/ionos-strings.xml @@ -0,0 +1,32 @@ + + + + + + + Storage permissions + Storage permissions + %1$s needs permission to access your files for better functionality. You can allow or deny access. + %1$s needs permission to access your files for better functionality. You can allow or deny access. + + + Name or email address… + + + Privacy Settings + This application uses cookies and similar technologies. By clicking on Agree, you consent to the processing of your data and also to its transfer to third parties. Visit our Privacy Policy for more information, also on data processing by third party providers. Click on Reject to prevent use of the tool, or change your selection at any time in Settings. + Settings + Agree + We collect anonymized data to optimize our app. We use software solutions by various partners for this purpose. Our intention is to offer full transparency and allow you to decide how your anonymized data is collected and used. You can also change your settings at any time later on under the Privacy menu item. Please note, however, that data collection contributes significantly to optimizing this app and that you would prevent these improvements by rejecting the transfer of data. + Data collection is necessary in order to use key functions of the app. + Necessary data collection + This data helps us to optimize your user experience and to identify system crashes and bugs. + Data analysis for needs-based design + Save Settings + + \ No newline at end of file diff --git a/app/src/main/res/values/ionos-styles.xml b/app/src/main/res/values/ionos-styles.xml new file mode 100644 index 000000000000..98038639857e --- /dev/null +++ b/app/src/main/res/values/ionos-styles.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + diff --git a/app/src/main/res/values/ionos-themes.xml b/app/src/main/res/values/ionos-themes.xml new file mode 100644 index 000000000000..aaf70af4ff65 --- /dev/null +++ b/app/src/main/res/values/ionos-themes.xml @@ -0,0 +1,30 @@ + + + + + + - @@ -113,7 +121,7 @@ @color/bg_default - @@ -227,7 +235,7 @@ @color/black @style/Theme.ownCloud.Overlay.ActionBar @style/ToolbarStyle.Overflow - @style/ThemeOverlay.App.BottomSheetDialog + @style/Generic.ThemeOverlay.App.BottomSheetDialog + diff --git a/scanbot/src/main/res/values-nl/strings.xml b/scanbot/src/main/res/values-nl/strings.xml new file mode 100644 index 000000000000..f800e1159e12 --- /dev/null +++ b/scanbot/src/main/res/values-nl/strings.xml @@ -0,0 +1,58 @@ + + + + + Annuleren + Er is een fout opgetreden. Probeer het later nog eens. + Houd ingedrukt en versleep om pagina\'s opnieuw te sorteren + Toegang tot de camera is vereist om de code te scannen + Onvoldoende ruimte + "Op de smartphone is onvoldoende opslagruimte beschikbaar." + OK + Opslaan + Opslaan als + Er is een fout opgetreden. Probeer het later nog eens + Automatisch + Geen document + Slecht licht + Te veel ruis op achtergrond + Niet bewegen + Perspectief + Dichterbij brengen + Flits + Importeren + Fout tijdens maken van document + Document detecteren + scan_%1$tY%1$tm%1$td-%1$tH%1$tM%1$tS + Voortgang gaat verloren + Document scannen + Kan bestand %s niet importeren + Filter wordt toegepast op alle afbeeldingen + Zwart/wit + Kleur + Grijsschaal + Magische kleur + Magische tekst + Geen filter + Voor alle toepassen + %d van %d + Onvoldoende ruimte op apparaat om foto te nemen. Maak ruimte vrij. + Document wordt verwerkt… + Verwerken… + Pagina\'s opnieuw sorteren + Randen resetten + Bestandsnaam: + Bestandstype: + Ongeldige bestandsnaam + Opslaglocatie: + Bijsnijden + Filteren + Draaien + Herschikken + Verwijderen + diff --git a/scanbot/src/main/res/values/attrs.xml b/scanbot/src/main/res/values/attrs.xml new file mode 100644 index 000000000000..078446388b23 --- /dev/null +++ b/scanbot/src/main/res/values/attrs.xml @@ -0,0 +1,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scanbot/src/main/res/values/colors-palette.xml b/scanbot/src/main/res/values/colors-palette.xml new file mode 100644 index 000000000000..43b859132093 --- /dev/null +++ b/scanbot/src/main/res/values/colors-palette.xml @@ -0,0 +1,26 @@ + + + + + #FFFFFF + #000000 + #333333 + #999999 + #dcdcdc + #EBEEEF + #222222 + #00FFFFFF + #0D000000 + #55FF8800 + #ccFF8800 + #a0c8e8 + #D9555555 + #8C0077BB + #0077BB + #003d8f + \ No newline at end of file diff --git a/scanbot/src/main/res/values/dimens.xml b/scanbot/src/main/res/values/dimens.xml new file mode 100644 index 000000000000..9ed9390a4d5b --- /dev/null +++ b/scanbot/src/main/res/values/dimens.xml @@ -0,0 +1,57 @@ + + + + + 4dp + 8dp + 12dp + 16dp + 24dp + + 18sp + + 1dp + + 56dp + + 2dp + 48dp + + 48dp + 10dp + 16dp + 36dp + + 64dp + + 20dp + 20dp + 30dp + + 4dp + 4dp + 24dp + 8dp + 11sp + 5dp + 16dp + 40dp + + 50dp + 250dp + 15dp + 20sp + 20dp + 10dp + 75dp + 18sp + + 480dp + + 20sp + \ No newline at end of file diff --git a/scanbot/src/main/res/values/preference_keys.xml b/scanbot/src/main/res/values/preference_keys.xml new file mode 100644 index 000000000000..def3f6a319f8 --- /dev/null +++ b/scanbot/src/main/res/values/preference_keys.xml @@ -0,0 +1,14 @@ + + + + + preference_scanbot_license_key + + preference_aes_gsm_key + preference_aes_gsm_initialization_vector + \ No newline at end of file diff --git a/scanbot/src/main/res/values/strings.xml b/scanbot/src/main/res/values/strings.xml new file mode 100644 index 000000000000..eabef060ff2c --- /dev/null +++ b/scanbot/src/main/res/values/strings.xml @@ -0,0 +1,60 @@ + + + + + Cancel + An error occurred. Please try again later. + Hold and drag to rearrange pages + Need use camera permissions to be granted to scan code + No enough space + "The smartphone does not have enough storage space available. " + OK + Save + Save as + An error occurred. Please try again later + + Automatic + No Document + Poor light + Background too noisy + Don\'t move + Perspective + Move closer + Flash + Import + Error while creating document + Detect document + scan_%1$tY%1$tm%1$td-%1$tH%1$tM%1$tS + Progress will be lost + Scan document + Can\'t import file %s + Applying filter for all images + Black & White + Color + Grayscale + Magic Color + Magic Text + No filter + Apply for all + %d of %d + Not enough space on device for taking photo. Please, clear storage. + Processing document… + Processing… + Rearrange pages + Reset borders + File name: + File type: + Invalid file name + Save location: + + Crop + Filter + Rotate + Rearrange + Delete + diff --git a/scanbot/src/main/res/values/styles.xml b/scanbot/src/main/res/values/styles.xml new file mode 100644 index 000000000000..a0f2eb4ff932 --- /dev/null +++ b/scanbot/src/main/res/values/styles.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + diff --git a/scanbot/src/main/res/values/theme.xml b/scanbot/src/main/res/values/theme.xml new file mode 100644 index 000000000000..fee2f03abf28 --- /dev/null +++ b/scanbot/src/main/res/values/theme.xml @@ -0,0 +1,71 @@ + + + + + diff --git a/scanbot/src/main/res/values/untranslatable_strings.xml b/scanbot/src/main/res/values/untranslatable_strings.xml new file mode 100644 index 000000000000..7d520ab5d97c --- /dev/null +++ b/scanbot/src/main/res/values/untranslatable_strings.xml @@ -0,0 +1,14 @@ + + + + + JPG + PDF + PDF (OCR) + PNG + \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 352229bd93d9..51ff9b3b9410 100644 --- a/settings.gradle +++ b/settings.gradle @@ -8,6 +8,7 @@ rootProject.name = 'Nextcloud' include ':app' include ':appscan' +include ':scanbot' //includeBuild('../android-common') { // dependencySubstitution {