diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index a2eecf2251..89ab9b612e 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,4 +1,4 @@ -FROM ubuntu:noble@sha256:e3f92abc0967a6c19d0dfa2d55838833e947b9d74edbcb0113e48535ad4be12a +FROM ubuntu:noble@sha256:2e863c44b718727c860746568e1d54afd13b2fa71b160f5cd9058fc436217b30 ARG DEBIAN_FRONTEND=noninteractive ENV ANDROID_HOME=/usr/lib/android-sdk diff --git a/.drone.yml b/.drone.yml index 22394e7d3f..94dc4921bb 100644 --- a/.drone.yml +++ b/.drone.yml @@ -36,7 +36,7 @@ services: image: ghcr.io/nextcloud/continuous-integration-shallow-server:latest # also change in updateScreenshots.sh environment: EVAL: true - SERVER_VERSION: 'stable27' + SERVER_VERSION: 'stable29' commands: - BRANCH="$SERVER_VERSION" /usr/local/bin/initnc.sh - echo 127.0.0.1 server >> /etc/hosts @@ -48,13 +48,13 @@ services: - su www-data -c "php /var/www/html/occ group:add users" - su www-data -c "php /var/www/html/occ group:adduser users user1" - su www-data -c "php /var/www/html/occ group:adduser users user2" - - su www-data -c "git clone -b $SERVER_VERSION https://github.com/nextcloud/activity.git /var/www/html/apps/activity/" + - su www-data -c "git clone --depth 1 -b $SERVER_VERSION https://github.com/nextcloud/activity.git /var/www/html/apps/activity/" - su www-data -c "php /var/www/html/occ app:enable activity" - - su www-data -c "git clone -b $SERVER_VERSION https://github.com/nextcloud/text.git /var/www/html/apps/text/" + - su www-data -c "git clone --depth 1 -b $SERVER_VERSION https://github.com/nextcloud/text.git /var/www/html/apps/text/" - su www-data -c "php /var/www/html/occ app:enable text" - - su www-data -c "git clone -b $SERVER_VERSION https://github.com/nextcloud/end_to_end_encryption.git /var/www/html/apps/end_to_end_encryption/" + - su www-data -c "git clone --depth 1 -b $SERVER_VERSION https://github.com/nextcloud/end_to_end_encryption.git /var/www/html/apps/end_to_end_encryption/" - su www-data -c "php /var/www/html/occ app:enable end_to_end_encryption" - - su www-data -c "git clone -b $SERVER_VERSION https://github.com/nextcloud/photos.git /var/www/html/apps/photos/" + - su www-data -c "git clone --depth 1 -b $SERVER_VERSION https://github.com/nextcloud/photos.git /var/www/html/apps/photos/" - su www-data -c "cd /var/www/html/apps/photos; composer install" - su www-data -c "php /var/www/html/occ app:enable -f photos" - /usr/local/bin/run.sh @@ -106,13 +106,13 @@ services: - su www-data -c "php /var/www/html/occ group:add users" - su www-data -c "php /var/www/html/occ group:adduser users user1" - su www-data -c "php /var/www/html/occ group:adduser users user2" - - su www-data -c "git clone -b master https://github.com/nextcloud/activity.git /var/www/html/apps/activity/" + - su www-data -c "git clone --depth 1 -b master https://github.com/nextcloud/activity.git /var/www/html/apps/activity/" - su www-data -c "php /var/www/html/occ app:enable activity" - - su www-data -c "git clone -b main https://github.com/nextcloud/text.git /var/www/html/apps/text/" + - su www-data -c "git clone --depth 1 -b main https://github.com/nextcloud/text.git /var/www/html/apps/text/" - su www-data -c "php /var/www/html/occ app:enable text" - - su www-data -c "git clone -b master https://github.com/nextcloud/end_to_end_encryption/ /var/www/html/apps/end_to_end_encryption/" + - su www-data -c "git clone --depth 1 -b master https://github.com/nextcloud/end_to_end_encryption/ /var/www/html/apps/end_to_end_encryption/" - su www-data -c "php /var/www/html/occ app:enable end_to_end_encryption" - - su www-data -c "git clone https://github.com/nextcloud/photos.git /var/www/html/apps/photos/" + - su www-data -c "git clone --depth 1 https://github.com/nextcloud/photos.git /var/www/html/apps/photos/" - su www-data -c "cd /var/www/html/apps/photos; composer install" - su www-data -c "php /var/www/html/occ app:enable -f photos" - /usr/local/bin/run.sh @@ -183,6 +183,6 @@ name: GIT_TOKEN data: XIoa9IYq+xQ+N5iln8dlpWv0jV6ROr7HuE24ioUr4uQ8m8SjyH0yognWYLYLqnbTKrFWlFZiEMQTH/sZiWjRFvV1iL0= --- kind: signature -hmac: 5d64f2d46fc49a1e7dad823b7ac9c0ee3762a748ab025782a20292887607d831 +hmac: a659f1f06a609743077c77b6a07a369e55db67adbd8343fc027ac1c9473d1a43 ... diff --git a/.github/workflows/screenShotTest.yml b/.github/workflows/screenShotTest.yml index 224e78be54..a2b8e633b0 100644 --- a/.github/workflows/screenShotTest.yml +++ b/.github/workflows/screenShotTest.yml @@ -56,7 +56,7 @@ jobs: - name: create AVD and generate snapshot for caching if: steps.avd-cache.outputs.cache-hit != 'true' - uses: reactivecircus/android-emulator-runner@6b0df4b0efb23bb0ec63d881db79aefbc976e4b2 # v2.30.1 + uses: reactivecircus/android-emulator-runner@77986be26589807b8ebab3fde7bbf5c60dabec32 # v2.31.0 with: api-level: ${{ matrix.api-level }} force-avd-creation: false @@ -84,7 +84,7 @@ jobs: - name: Run screenshot tests env: SHOT_TEST: "true" - uses: reactivecircus/android-emulator-runner@6b0df4b0efb23bb0ec63d881db79aefbc976e4b2 # v2.30.1 + uses: reactivecircus/android-emulator-runner@77986be26589807b8ebab3fde7bbf5c60dabec32 # v2.31.0 with: api-level: ${{ matrix.api-level }} force-avd-creation: false diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml index dcebd2c302..47f4f4c4a8 100644 --- a/.github/workflows/unit-tests.yml +++ b/.github/workflows/unit-tests.yml @@ -33,7 +33,7 @@ jobs: if: ${{ always() }} run: scripts/deleteOldComments.sh "test" "Unit" ${{github.event.number}} - name: Run unit tests with coverage - uses: gradle/gradle-build-action@4c39dd82cd5e1ec7c6fa0173bb41b4b6bb3b86ff # v3.3.2 + uses: gradle/gradle-build-action@66535aaf56f831b35e3a8481c9c99b665b84dd45 # v3.4.2 with: arguments: jacocoTestGplayDebugUnitTest - name: Upload failing results diff --git a/app/build.gradle b/app/build.gradle index 9605307e6c..6fa662b64c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,7 +16,7 @@ import org.gradle.internal.jvm.Jvm buildscript { dependencies { classpath "com.android.tools.build:gradle:$androidPluginVersion" - classpath 'com.github.spotbugs.snom:spotbugs-gradle-plugin:6.0.16' + classpath 'com.github.spotbugs.snom:spotbugs-gradle-plugin:6.0.18' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.23.6" classpath "commons-httpclient:commons-httpclient:3.1@jar" // remove after entire switch to lib v2 diff --git a/app/src/main/java/com/nextcloud/ui/ChooseAccountDialogFragment.kt b/app/src/main/java/com/nextcloud/ui/ChooseAccountDialogFragment.kt index 4588cf03cd..d7d5d4de41 100644 --- a/app/src/main/java/com/nextcloud/ui/ChooseAccountDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/ui/ChooseAccountDialogFragment.kt @@ -227,7 +227,7 @@ class ChooseAccountDialogFragment : binding.currentAccount.status.let { if (newStatus.message.isNullOrBlank()) { - it.text = "" + it.text = getString(R.string.empty) it.visibility = View.GONE } else { it.text = newStatus.message diff --git a/app/src/main/java/com/owncloud/android/ui/activity/PassCodeActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/PassCodeActivity.kt index cf14a992e4..5a2365fe80 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/PassCodeActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/PassCodeActivity.kt @@ -188,7 +188,7 @@ class PassCodeActivity : AppCompatActivity(), Injectable { passCodeDigits?.set(passCodeIndex - 1, "") } - passCodeEditTexts[passCodeIndex - 1]?.setText("") + passCodeEditTexts[passCodeIndex - 1]?.setText(R.string.empty) changed = false } else if (!changed) { diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/CreateFolderDialogFragment.kt b/app/src/main/java/com/owncloud/android/ui/dialog/CreateFolderDialogFragment.kt index d2ebcfafe5..463ea2c40a 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/CreateFolderDialogFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/CreateFolderDialogFragment.kt @@ -92,7 +92,7 @@ class CreateFolderDialogFragment : DialogFragment(), DialogInterface.OnClickList val view: View = binding.root // Setup layout - binding.userInput.setText("") + binding.userInput.setText(R.string.empty) viewThemeUtils?.material?.colorTextInputLayout(binding.userInputContainer) val parentFolder = requireArguments().getParcelableArgument(ARG_PARENT_FOLDER, OCFile::class.java) diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.kt b/app/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.kt index aada6af1ad..2c73b3a92d 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/SharePasswordDialogFragment.kt @@ -112,7 +112,7 @@ class SharePasswordDialogFragment : DialogFragment(), Injectable { binding = PasswordDialogBinding.inflate(inflater, null, false) // Setup layout - binding?.sharePassword?.setText("") + binding?.sharePassword?.setText(R.string.empty) viewThemeUtils?.material?.colorTextInputLayout(binding!!.sharePasswordContainer) val neutralButtonTextId: Int diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt index 2296b67fb9..119d4e68be 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailsSharingProcessFragment.kt @@ -402,7 +402,7 @@ class FileDetailsSharingProcessFragment : binding.noteText.setText(share?.note) } else { binding.shareProcessBtnNext.text = requireContext().resources.getString(R.string.send_share) - binding.noteText.setText("") + binding.noteText.setText(R.string.empty) } shareProcessStep = SCREEN_TYPE_NOTE } @@ -447,7 +447,7 @@ class FileDetailsSharingProcessFragment : private fun showChangeNameInput(isChecked: Boolean) { binding.shareProcessChangeNameContainer.visibility = if (isChecked) View.VISIBLE else View.GONE if (!isChecked) { - binding.shareProcessChangeName.setText("") + binding.shareProcessChangeName.setText(R.string.empty) } } @@ -474,7 +474,7 @@ class FileDetailsSharingProcessFragment : // reset the expiration date if switch is unchecked if (!isChecked) { chosenExpDateInMills = -1 - binding.shareProcessSelectExpDate.text = "" + binding.shareProcessSelectExpDate.text = getString(R.string.empty) } } @@ -483,7 +483,7 @@ class FileDetailsSharingProcessFragment : // reset the password if switch is unchecked if (!isChecked) { - binding.shareProcessEnterPassword.setText("") + binding.shareProcessEnterPassword.setText(R.string.empty) } } diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java deleted file mode 100644 index f871ed7823..0000000000 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java +++ /dev/null @@ -1,536 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2020-2024 Andy Scherzinger - * SPDX-FileCopyrightText: 2023 Alper Ozturk - * SPDX-FileCopyrightText: 2022 Álvaro Brey - * SPDX-FileCopyrightText: 2019 Tobias Kaminsky - * SPDX-FileCopyrightText: 2019 Chris Narkiewicz - * SPDX-FileCopyrightText: 2016 ownCloud Inc. - * SPDX-FileCopyrightText: 2015 María Asensio Valverde - * SPDX-FileCopyrightText: 2013 David A. Velasco - * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) - */ -package com.owncloud.android.ui.preview; - -import android.annotation.SuppressLint; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.Bundle; -import android.view.MenuItem; -import android.view.View; - -import com.nextcloud.client.account.User; -import com.nextcloud.client.di.Injectable; -import com.nextcloud.client.editimage.EditImageActivity; -import com.nextcloud.client.jobs.download.FileDownloadHelper; -import com.nextcloud.client.jobs.download.FileDownloadWorker; -import com.nextcloud.client.jobs.upload.FileUploadWorker; -import com.nextcloud.client.preferences.AppPreferences; -import com.nextcloud.model.WorkerState; -import com.nextcloud.model.WorkerStateLiveData; -import com.nextcloud.utils.extensions.IntentExtensionsKt; -import com.owncloud.android.MainApp; -import com.owncloud.android.R; -import com.owncloud.android.datamodel.FileDataStorageManager; -import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.datamodel.VirtualFolderType; -import com.owncloud.android.lib.common.operations.OnRemoteOperationListener; -import com.owncloud.android.lib.common.operations.RemoteOperation; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.operations.RemoveFileOperation; -import com.owncloud.android.operations.SynchronizeFileOperation; -import com.owncloud.android.ui.activity.FileActivity; -import com.owncloud.android.ui.activity.FileDisplayActivity; -import com.owncloud.android.ui.fragment.FileFragment; -import com.owncloud.android.ui.fragment.GalleryFragment; -import com.owncloud.android.ui.fragment.OCFileListFragment; -import com.owncloud.android.utils.MimeTypeUtil; - -import java.io.Serializable; -import java.util.Optional; - -import javax.inject.Inject; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.ActionBar; -import androidx.drawerlayout.widget.DrawerLayout; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; -import androidx.viewpager2.widget.ViewPager2; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; - -/** - * Holds a swiping gallery where image files contained in an Nextcloud directory are shown. - */ -@SuppressWarnings("PMD.AvoidDuplicateLiterals") -public class PreviewImageActivity extends FileActivity implements - FileFragment.ContainerActivity, - OnRemoteOperationListener, - Injectable { - - public static final String TAG = PreviewImageActivity.class.getSimpleName(); - public static final String EXTRA_VIRTUAL_TYPE = "EXTRA_VIRTUAL_TYPE"; - private static final String KEY_WAITING_FOR_BINDER = "WAITING_FOR_BINDER"; - private static final String KEY_SYSTEM_VISIBLE = "TRUE"; - - private OCFile livePhotoFile; - private ViewPager2 viewPager; - private PreviewImagePagerAdapter previewImagePagerAdapter; - private int savedPosition; - private boolean hasSavedPosition; - private boolean requestWaitingForBinder; - private DownloadFinishReceiver downloadFinishReceiver; - private View fullScreenAnchorView; - private boolean isDownloadWorkStarted = false; - - @Inject AppPreferences preferences; - @Inject LocalBroadcastManager localBroadcastManager; - - private ActionBar actionBar; - - public static Intent previewFileIntent(Context context, User user, OCFile file) { - final Intent intent = new Intent(context, PreviewImageActivity.class); - intent.putExtra(FileActivity.EXTRA_FILE, file); - intent.putExtra(FileActivity.EXTRA_USER, user); - return intent; - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - actionBar = getSupportActionBar(); - - if (savedInstanceState != null && !savedInstanceState.getBoolean(KEY_SYSTEM_VISIBLE, true) && - actionBar != null) { - actionBar.hide(); - } - - setContentView(R.layout.preview_image_activity); - - livePhotoFile = IntentExtensionsKt.getParcelableArgument(getIntent(), EXTRA_LIVE_PHOTO_FILE, OCFile.class); - - // Navigation Drawer - setupDrawer(); - - // ActionBar - OCFile chosenFile = IntentExtensionsKt.getParcelableArgument(getIntent(), FileActivity.EXTRA_FILE, OCFile.class); - updateActionBarTitleAndHomeButton(chosenFile); - - if (actionBar != null) { - viewThemeUtils.files.setWhiteBackButton(this, actionBar); - actionBar.setDisplayHomeAsUpEnabled(true); - } - - fullScreenAnchorView = getWindow().getDecorView(); - // to keep our UI controls visibility in line with system bars visibility - setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); - - if (savedInstanceState != null) { - requestWaitingForBinder = savedInstanceState.getBoolean(KEY_WAITING_FOR_BINDER); - } else { - requestWaitingForBinder = false; - } - - observeWorkerState(); - } - - public void toggleActionBarVisibility(boolean hide) { - if (actionBar == null) { - return; - } - - if (hide) { - actionBar.hide(); - } else { - actionBar.show(); - } - } - - private void initViewPager(User user) { - // virtual folder - final Serializable virtualFolderType = IntentExtensionsKt.getSerializableArgument(getIntent(), EXTRA_VIRTUAL_TYPE, Serializable.class); - if (virtualFolderType != null && virtualFolderType != VirtualFolderType.NONE) { - VirtualFolderType type = (VirtualFolderType) virtualFolderType; - - previewImagePagerAdapter = new PreviewImagePagerAdapter(this, - type, - user, - getStorageManager()); - } else { - // get parent from path - OCFile parentFolder = getStorageManager().getFileById(getFile().getParentId()); - - if (parentFolder == null) { - // should not be necessary - parentFolder = getStorageManager().getFileByEncryptedRemotePath(OCFile.ROOT_PATH); - } - - previewImagePagerAdapter = new PreviewImagePagerAdapter( - this, - livePhotoFile, - parentFolder, - user, - getStorageManager(), - MainApp.isOnlyOnDevice(), - preferences - ); - } - - viewPager = findViewById(R.id.fragmentPager); - - int position = hasSavedPosition ? savedPosition : previewImagePagerAdapter.getFilePosition(getFile()); - position = Math.max(position, 0); - - viewPager.setAdapter(previewImagePagerAdapter); - viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { - @Override - public void onPageSelected(int position) { - selectPage(position); - } - }); - viewPager.setCurrentItem(position, false); - - if (position == 0 && !getFile().isDown()) { - // this is necessary because mViewPager.setCurrentItem(0) just after setting the - // adapter does not result in a call to #onPageSelected(0) - requestWaitingForBinder = true; - } - } - - @Override - public void onBackPressed() { - sendRefreshSearchEventBroadcast(); - super.onBackPressed(); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == android.R.id.home) { - sendRefreshSearchEventBroadcast(); - - if (isDrawerOpen()) { - closeDrawer(); - } else { - backToDisplayActivity(); - } - return true; - } else { - return super.onOptionsItemSelected(item); - } - } - - private void sendRefreshSearchEventBroadcast() { - Intent intent = new Intent(GalleryFragment.REFRESH_SEARCH_EVENT_RECEIVER); - LocalBroadcastManager.getInstance(this).sendBroadcast(intent); - } - - @Override - public void onStart() { - super.onStart(); - Optional optionalUser = getUser(); - if (optionalUser.isPresent()) { - OCFile file = getFile(); - /// Validate handled file (first image to preview) - if (file == null) { - throw new IllegalStateException("Instanced with a NULL OCFile"); - } - if (!MimeTypeUtil.isImage(file)) { - throw new IllegalArgumentException("Non-image file passed as argument"); - } - - // Update file according to DB file, if it is possible - if (file.getFileId() > FileDataStorageManager.ROOT_PARENT_ID) { - file = getStorageManager().getFileById(file.getFileId()); - } - - if (file != null) { - /// Refresh the activity according to the Account and OCFile set - setFile(file); // reset after getting it fresh from storageManager - updateActionBarTitle(getFile().getFileName()); - //if (!stateWasRecovered) { - initViewPager(optionalUser.get()); - //} - - } else { - // handled file not in the current Account - finish(); - } - } - } - - @Override - protected void onSaveInstanceState(@NonNull Bundle outState) { - super.onSaveInstanceState(outState); - outState.putBoolean(KEY_WAITING_FOR_BINDER, requestWaitingForBinder); - outState.putBoolean(KEY_SYSTEM_VISIBLE, isSystemUIVisible()); - } - - @Override - public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) { - super.onRemoteOperationFinish(operation, result); - - if (operation instanceof RemoveFileOperation) { - int deletePosition = viewPager.getCurrentItem(); - int nextPosition = deletePosition > 0 ? deletePosition - 1 : 0; - - if (previewImagePagerAdapter.getItemCount() <= 1) { - finish(); - return; - } - - viewPager.setCurrentItem(nextPosition, true); - previewImagePagerAdapter.delete(deletePosition); - } else if (operation instanceof SynchronizeFileOperation) { - onSynchronizeFileOperationFinish(result); - } - } - - private void onSynchronizeFileOperationFinish(RemoteOperationResult result) { - if (result.isSuccess()) { - supportInvalidateOptionsMenu(); - } - } - - private void observeWorkerState() { - WorkerStateLiveData.Companion.instance().observe(this, state -> { - if (state instanceof WorkerState.Download) { - Log_OC.d(TAG, "Download worker started"); - isDownloadWorkStarted = true; - - if (requestWaitingForBinder) { - requestWaitingForBinder = false; - Log_OC.d(TAG, "Simulating reselection of current page after connection " + - "of download binder"); - selectPage(viewPager.getCurrentItem()); - } - } else { - Log_OC.d(TAG, "Download worker stopped"); - isDownloadWorkStarted = false; - } - }); - } - - @Override - public void onStop() { - super.onStop(); - } - - @Override - public void onDestroy() { - super.onDestroy(); - } - - @Override - protected void onResume() { - super.onResume(); - - downloadFinishReceiver = new DownloadFinishReceiver(); - IntentFilter downloadIntentFilter = new IntentFilter(FileDownloadWorker.Companion.getDownloadFinishMessage()); - localBroadcastManager.registerReceiver(downloadFinishReceiver, downloadIntentFilter); - - UploadFinishReceiver uploadFinishReceiver = new UploadFinishReceiver(); - IntentFilter uploadIntentFilter = new IntentFilter(FileUploadWorker.Companion.getUploadFinishMessage()); - localBroadcastManager.registerReceiver(uploadFinishReceiver, uploadIntentFilter); - } - - @Override - protected void onPostResume() { - super.onPostResume(); - } - - @Override - public void onPause() { - if (downloadFinishReceiver != null){ - localBroadcastManager.unregisterReceiver(downloadFinishReceiver); - downloadFinishReceiver = null; - } - - super.onPause(); - } - - - private void backToDisplayActivity() { - finish(); - } - - @SuppressFBWarnings("DLS") - @Override - public void showDetails(OCFile file) { - final Intent showDetailsIntent = new Intent(this, FileDisplayActivity.class); - showDetailsIntent.setAction(FileDisplayActivity.ACTION_DETAILS); - showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, file); - showDetailsIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - startActivity(showDetailsIntent); - finish(); - } - - @Override - public void showDetails(OCFile file, int activeTab) { - showDetails(file); - } - - public void requestForDownload(OCFile file) { - requestForDownload(file, null); - } - - public void requestForDownload(OCFile file, String downloadBehaviour) { - final User user = getUser().orElseThrow(RuntimeException::new); - FileDownloadHelper.Companion.instance().downloadFileIfNotStartedBefore(user, file); - } - - /** - * This method will be invoked when a new page becomes selected. Animation is not necessarily - * complete. - * - * @param position Position index of the new selected page - */ - public void selectPage(int position) { - savedPosition = position; - hasSavedPosition = true; - - OCFile currentFile = previewImagePagerAdapter.getFileAt(position); - - if (!isDownloadWorkStarted) { - requestWaitingForBinder = true; - } else { - if (currentFile != null) { - if (currentFile.isEncrypted() && !currentFile.isDown() && - !previewImagePagerAdapter.pendingErrorAt(position)) { - requestForDownload(currentFile); - } - - // Call to reset image zoom to initial state - // ((PreviewImagePagerAdapter) mViewPager.getAdapter()).resetZoom(); - } - } - - if (currentFile != null) { - updateActionBarTitle(currentFile.getFileName()); - setDrawerIndicatorEnabled(false); - } - } - - public void updateActionBarTitle(String title) { - if (getSupportActionBar() != null) { - getSupportActionBar().setTitle(title); - } - } - - /** - * Class waiting for broadcast events from the {@link FileDownloadWorker} service. - *

- * Updates the UI when a download is started or finished, provided that it is relevant for the - * folder displayed in the gallery. - */ - private class DownloadFinishReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - previewNewImage(intent); - } - } - - private class UploadFinishReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - previewNewImage(intent); - } - } - - private void previewNewImage(Intent intent) { - String accountName = intent.getStringExtra(FileDownloadWorker.EXTRA_ACCOUNT_NAME); - String downloadedRemotePath = intent.getStringExtra(FileDownloadWorker.EXTRA_REMOTE_PATH); - String downloadBehaviour = intent.getStringExtra(OCFileListFragment.DOWNLOAD_BEHAVIOUR); - - if (getAccount().name.equals(accountName) && downloadedRemotePath != null) { - OCFile file = getStorageManager().getFileByEncryptedRemotePath(downloadedRemotePath); - boolean downloadWasFine = intent.getBooleanExtra(FileDownloadWorker.EXTRA_DOWNLOAD_RESULT, false); - - if (EditImageActivity.OPEN_IMAGE_EDITOR.equals(downloadBehaviour)) { - startImageEditor(file); - } else { - int position = previewImagePagerAdapter.getFilePosition(file); - if (position >= 0) { - if (downloadWasFine) { - previewImagePagerAdapter.updateFile(position, file); - } else { - previewImagePagerAdapter.updateWithDownloadError(position); - } - previewImagePagerAdapter.notifyItemChanged(position); - } else if (downloadWasFine) { - Optional user = getUser(); - - if (user.isPresent()) { - initViewPager(user.get()); - int newPosition = previewImagePagerAdapter.getFilePosition(file); - if (newPosition >= 0) { - viewPager.setCurrentItem(newPosition); - } - } - } - } - } - } - - public boolean isSystemUIVisible() { - return getSupportActionBar() == null || getSupportActionBar().isShowing(); - } - - public void toggleFullScreen() { - boolean visible = (fullScreenAnchorView.getSystemUiVisibility() - & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0; - - if (visible) { - hideSystemUI(fullScreenAnchorView); - } else { - showSystemUI(fullScreenAnchorView); - } - } - - public void startImageEditor(OCFile file) { - if (file.isDown()) { - Intent editImageIntent = new Intent(this, EditImageActivity.class); - editImageIntent.putExtra(EditImageActivity.EXTRA_FILE, file); - startActivity(editImageIntent); - } else { - requestForDownload(file, EditImageActivity.OPEN_IMAGE_EDITOR); - } - } - - @Override - public void onBrowsedDownTo(OCFile folder) { - // TODO Auto-generated method stub - - } - - @Override - public void onTransferStateChanged(OCFile file, boolean downloading, boolean uploading) { - // TODO Auto-generated method stub - - } - - - @SuppressLint("InlinedApi") - private void hideSystemUI(View anchorView) { - anchorView.setSystemUiVisibility( - View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hides NAVIGATION BAR; Android >= 4.0 - | View.SYSTEM_UI_FLAG_FULLSCREEN // hides STATUS BAR; Android >= 4.1 - | View.SYSTEM_UI_FLAG_IMMERSIVE // stays interactive; Android >= 4.4 - | View.SYSTEM_UI_FLAG_LAYOUT_STABLE // draw full window; Android >= 4.1 - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN // draw full window; Android >= 4.1 - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION // draw full window; Android >= 4.1 - ); - } - - @SuppressLint("InlinedApi") - private void showSystemUI(View anchorView) { - anchorView.setSystemUiVisibility( - View.SYSTEM_UI_FLAG_LAYOUT_STABLE // draw full window; Android >= 4.1 - | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN // draw full window; Android >= 4.1 - | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION // draw full window; Android >= 4. - ); - } -} 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 new file mode 100644 index 0000000000..e535519213 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt @@ -0,0 +1,527 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2020-2024 Andy Scherzinger + * SPDX-FileCopyrightText: 2023 Alper Ozturk + * SPDX-FileCopyrightText: 2022 Álvaro Brey + * SPDX-FileCopyrightText: 2019 Tobias Kaminsky + * SPDX-FileCopyrightText: 2019 Chris Narkiewicz + * SPDX-FileCopyrightText: 2016 ownCloud Inc. + * SPDX-FileCopyrightText: 2015 María Asensio Valverde + * SPDX-FileCopyrightText: 2013 David A. Velasco + * SPDX-License-Identifier: GPL-2.0-only AND (AGPL-3.0-or-later OR GPL-2.0-only) + */ +package com.owncloud.android.ui.preview + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.os.Build +import android.os.Bundle +import android.view.MenuItem +import android.view.View +import android.view.WindowInsets +import android.view.WindowInsetsController +import androidx.appcompat.app.ActionBar +import androidx.drawerlayout.widget.DrawerLayout +import androidx.localbroadcastmanager.content.LocalBroadcastManager +import androidx.viewpager2.widget.ViewPager2 +import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback +import com.nextcloud.client.account.User +import com.nextcloud.client.di.Injectable +import com.nextcloud.client.editimage.EditImageActivity +import com.nextcloud.client.jobs.download.FileDownloadHelper +import com.nextcloud.client.jobs.download.FileDownloadWorker +import com.nextcloud.client.jobs.download.FileDownloadWorker.Companion.getDownloadFinishMessage +import com.nextcloud.client.jobs.upload.FileUploadWorker.Companion.getUploadFinishMessage +import com.nextcloud.client.preferences.AppPreferences +import com.nextcloud.model.WorkerState +import com.nextcloud.model.WorkerStateLiveData +import com.nextcloud.utils.extensions.getParcelableArgument +import com.nextcloud.utils.extensions.getSerializableArgument +import com.owncloud.android.MainApp +import com.owncloud.android.R +import com.owncloud.android.datamodel.FileDataStorageManager +import com.owncloud.android.datamodel.OCFile +import com.owncloud.android.datamodel.VirtualFolderType +import com.owncloud.android.lib.common.operations.OnRemoteOperationListener +import com.owncloud.android.lib.common.operations.RemoteOperation +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.operations.RemoveFileOperation +import com.owncloud.android.operations.SynchronizeFileOperation +import com.owncloud.android.ui.activity.FileActivity +import com.owncloud.android.ui.activity.FileDisplayActivity +import com.owncloud.android.ui.fragment.FileFragment +import com.owncloud.android.ui.fragment.GalleryFragment +import com.owncloud.android.ui.fragment.OCFileListFragment +import com.owncloud.android.utils.MimeTypeUtil +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings +import java.io.Serializable +import javax.inject.Inject +import kotlin.math.max + +/** + * Holds a swiping gallery where image files contained in an Nextcloud directory are shown. + */ +class PreviewImageActivity : FileActivity(), FileFragment.ContainerActivity, OnRemoteOperationListener, Injectable { + private var livePhotoFile: OCFile? = null + private var viewPager: ViewPager2? = null + private var previewImagePagerAdapter: PreviewImagePagerAdapter? = null + private var savedPosition = 0 + private var hasSavedPosition = false + private var requestWaitingForBinder = false + private var downloadFinishReceiver: DownloadFinishReceiver? = null + private var fullScreenAnchorView: View? = null + private var isDownloadWorkStarted = false + + @Inject + lateinit var preferences: AppPreferences + + @Inject + lateinit var localBroadcastManager: LocalBroadcastManager + + private var actionBar: ActionBar? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + actionBar = supportActionBar + + if (savedInstanceState != null && !savedInstanceState.getBoolean( + KEY_SYSTEM_VISIBLE, + true + ) && actionBar != null + ) { + actionBar?.hide() + } + + setContentView(R.layout.preview_image_activity) + + livePhotoFile = intent.getParcelableArgument(EXTRA_LIVE_PHOTO_FILE, OCFile::class.java) + + setupDrawer() + + val chosenFile = intent.getParcelableArgument(EXTRA_FILE, OCFile::class.java) + updateActionBarTitleAndHomeButton(chosenFile) + + if (actionBar != null) { + viewThemeUtils.files.setWhiteBackButton(this, actionBar!!) + actionBar?.setDisplayHomeAsUpEnabled(true) + } + + fullScreenAnchorView = window.decorView + // to keep our UI controls visibility in line with system bars visibility + setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED) + + requestWaitingForBinder = savedInstanceState?.getBoolean(KEY_WAITING_FOR_BINDER) ?: false + + observeWorkerState() + } + + fun toggleActionBarVisibility(hide: Boolean) { + if (actionBar == null) { + return + } + + if (hide) { + actionBar?.hide() + } else { + actionBar?.show() + } + } + + private fun initViewPager(user: User) { + val virtualFolderType = intent.getSerializableArgument(EXTRA_VIRTUAL_TYPE, Serializable::class.java) + if (virtualFolderType != null && virtualFolderType !== VirtualFolderType.NONE) { + val type = virtualFolderType as VirtualFolderType + + previewImagePagerAdapter = PreviewImagePagerAdapter( + this, + type, + user, + storageManager + ) + } else { + // get parent from path + var parentFolder = storageManager.getFileById(file.parentId) + + if (parentFolder == null) { + // should not be necessary + parentFolder = storageManager.getFileByEncryptedRemotePath(OCFile.ROOT_PATH) + } + + previewImagePagerAdapter = PreviewImagePagerAdapter( + this, + livePhotoFile, + parentFolder, + user, + storageManager, + MainApp.isOnlyOnDevice(), + preferences + ) + } + + viewPager = findViewById(R.id.fragmentPager) + + var position = if (hasSavedPosition) savedPosition else previewImagePagerAdapter?.getFilePosition(file) + position = position?.toDouble()?.let { max(it, 0.0).toInt() } + + viewPager?.setAdapter(previewImagePagerAdapter) + viewPager?.registerOnPageChangeCallback(object : OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + selectPage(position) + } + }) + if (position != null) { + viewPager?.setCurrentItem(position, false) + } + + if (position == 0 && !file.isDown) { + // this is necessary because mViewPager.setCurrentItem(0) just after setting the + // adapter does not result in a call to #onPageSelected(0) + requestWaitingForBinder = true + } + } + + override fun onBackPressed() { + sendRefreshSearchEventBroadcast() + super.onBackPressed() + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId != android.R.id.home) { + return super.onOptionsItemSelected(item) + } + + sendRefreshSearchEventBroadcast() + + if (isDrawerOpen) { + closeDrawer() + } else { + backToDisplayActivity() + } + + return true + } + + private fun sendRefreshSearchEventBroadcast() { + val intent = Intent(GalleryFragment.REFRESH_SEARCH_EVENT_RECEIVER) + LocalBroadcastManager.getInstance(this).sendBroadcast(intent) + } + + public override fun onStart() { + super.onStart() + val optionalUser = user + if (optionalUser.isPresent) { + var file: OCFile? = file ?: throw IllegalStateException("Instanced with a NULL OCFile") + // / Validate handled file (first image to preview) + require(MimeTypeUtil.isImage(file)) { "Non-image file passed as argument" } + + // Update file according to DB file, if it is possible + if (file!!.fileId > FileDataStorageManager.ROOT_PARENT_ID) { + file = storageManager.getFileById(file.fileId) + } + + if (file != null) { + // / Refresh the activity according to the Account and OCFile set + setFile(file) // reset after getting it fresh from storageManager + updateActionBarTitle(getFile().fileName) + // if (!stateWasRecovered) { + initViewPager(optionalUser.get()) + + // } + } else { + // handled file not in the current Account + finish() + } + } + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putBoolean(KEY_WAITING_FOR_BINDER, requestWaitingForBinder) + outState.putBoolean(KEY_SYSTEM_VISIBLE, isSystemUIVisible) + } + + override fun onRemoteOperationFinish(operation: RemoteOperation<*>?, result: RemoteOperationResult<*>) { + super.onRemoteOperationFinish(operation, result) + + if (operation is RemoveFileOperation) { + val deletePosition = viewPager?.currentItem ?: return + val nextPosition = if (deletePosition > 0) deletePosition - 1 else 0 + + previewImagePagerAdapter?.let { + if (it.itemCount <= 1) { + finish() + return + } + } + + viewPager?.setCurrentItem(nextPosition, true) + previewImagePagerAdapter?.delete(deletePosition) + } else if (operation is SynchronizeFileOperation) { + onSynchronizeFileOperationFinish(result) + } + } + + private fun onSynchronizeFileOperationFinish(result: RemoteOperationResult<*>) { + if (result.isSuccess) { + supportInvalidateOptionsMenu() + } + } + + private fun observeWorkerState() { + WorkerStateLiveData.instance().observe(this) { state: WorkerState? -> + if (state is WorkerState.Download) { + Log_OC.d(TAG, "Download worker started") + isDownloadWorkStarted = true + + if (requestWaitingForBinder) { + requestWaitingForBinder = false + Log_OC.d( + TAG, + "Simulating reselection of current page after connection " + + "of download binder" + ) + selectPage(viewPager?.currentItem) + } + } else { + Log_OC.d(TAG, "Download worker stopped") + isDownloadWorkStarted = false + } + } + } + + override fun onResume() { + super.onResume() + + downloadFinishReceiver = DownloadFinishReceiver() + val downloadIntentFilter = IntentFilter(getDownloadFinishMessage()) + localBroadcastManager.registerReceiver(downloadFinishReceiver!!, downloadIntentFilter) + + val uploadFinishReceiver = UploadFinishReceiver() + val uploadIntentFilter = IntentFilter(getUploadFinishMessage()) + localBroadcastManager.registerReceiver(uploadFinishReceiver, uploadIntentFilter) + } + + public override fun onPause() { + if (downloadFinishReceiver != null) { + localBroadcastManager.unregisterReceiver(downloadFinishReceiver!!) + downloadFinishReceiver = null + } + + super.onPause() + } + + private fun backToDisplayActivity() { + finish() + } + + @SuppressFBWarnings("DLS") + override fun showDetails(file: OCFile) { + val intent = Intent(this, FileDisplayActivity::class.java).apply { + setAction(FileDisplayActivity.ACTION_DETAILS) + putExtra(EXTRA_FILE, file) + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + } + + startActivity(intent) + finish() + } + + override fun showDetails(file: OCFile, activeTab: Int) { + showDetails(file) + } + + @JvmOverloads + fun requestForDownload(file: OCFile?, downloadBehaviour: String? = null) { + if (file == null) return + val user = user.orElseThrow { RuntimeException() } + FileDownloadHelper.instance().downloadFileIfNotStartedBefore(user, file) + } + + /** + * This method will be invoked when a new page becomes selected. Animation is not necessarily + * complete. + * + * @param position Position index of the new selected page + */ + fun selectPage(position: Int?) { + if (position == null) return + savedPosition = position + hasSavedPosition = true + + val currentFile = previewImagePagerAdapter?.getFileAt(position) + + if (!isDownloadWorkStarted) { + requestWaitingForBinder = true + } else { + if (currentFile != null) { + if (currentFile.isEncrypted && !currentFile.isDown && + previewImagePagerAdapter?.pendingErrorAt(position) == false + ) { + requestForDownload(currentFile) + } + + // Call to reset image zoom to initial state + // ((PreviewImagePagerAdapter) mViewPager.getAdapter()).resetZoom(); + } + } + + if (currentFile != null) { + updateActionBarTitle(currentFile.fileName) + setDrawerIndicatorEnabled(false) + } + } + + private fun updateActionBarTitle(title: String?) { + supportActionBar?.title = title + } + + /** + * Class waiting for broadcast events from the [FileDownloadWorker] service. + * + * + * Updates the UI when a download is started or finished, provided that it is relevant for the + * folder displayed in the gallery. + */ + private inner class DownloadFinishReceiver : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + previewNewImage(intent) + } + } + + private inner class UploadFinishReceiver : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + previewNewImage(intent) + } + } + + @Suppress("NestedBlockDepth", "ReturnCount") + private fun previewNewImage(intent: Intent) { + val accountName = intent.getStringExtra(FileDownloadWorker.EXTRA_ACCOUNT_NAME) + val downloadedRemotePath = intent.getStringExtra(FileDownloadWorker.EXTRA_REMOTE_PATH) + val downloadBehaviour = intent.getStringExtra(OCFileListFragment.DOWNLOAD_BEHAVIOUR) + + if (account.name != accountName || downloadedRemotePath == null) { + return + } + + val file = storageManager.getFileByEncryptedRemotePath(downloadedRemotePath) + val downloadWasFine = intent.getBooleanExtra(FileDownloadWorker.EXTRA_DOWNLOAD_RESULT, false) + + if (EditImageActivity.OPEN_IMAGE_EDITOR == downloadBehaviour) { + startImageEditor(file) + } else { + val position = previewImagePagerAdapter?.getFilePosition(file) ?: return + + if (position >= 0) { + if (downloadWasFine) { + previewImagePagerAdapter?.updateFile(position, file) + } else { + previewImagePagerAdapter?.updateWithDownloadError(position) + } + previewImagePagerAdapter?.notifyItemChanged(position) + } else if (downloadWasFine) { + val user = user + + if (user.isPresent) { + initViewPager(user.get()) + val newPosition = previewImagePagerAdapter?.getFilePosition(file) ?: return + if (newPosition >= 0) { + viewPager?.currentItem = newPosition + } + } + } + } + } + + val isSystemUIVisible: Boolean + get() = supportActionBar == null || supportActionBar?.isShowing == true + + fun toggleFullScreen() { + if (fullScreenAnchorView == null) return + val visible = ( + fullScreenAnchorView!!.systemUiVisibility + and View.SYSTEM_UI_FLAG_HIDE_NAVIGATION + ) == 0 + + if (visible) { + hideSystemUI(fullScreenAnchorView!!) + } else { + showSystemUI(fullScreenAnchorView!!) + } + } + + fun startImageEditor(file: OCFile) { + if (file.isDown) { + val editImageIntent = Intent(this, EditImageActivity::class.java) + editImageIntent.putExtra(EditImageActivity.EXTRA_FILE, file) + startActivity(editImageIntent) + } else { + requestForDownload(file, EditImageActivity.OPEN_IMAGE_EDITOR) + } + } + + override fun onBrowsedDownTo(folder: OCFile) { + // TODO Auto-generated method stub + } + + override fun onTransferStateChanged(file: OCFile, downloading: Boolean, uploading: Boolean) { + // TODO Auto-generated method stub + } + + private fun hideSystemUI(anchorView: View) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + window.insetsController?.let { controller -> + controller.hide(WindowInsets.Type.systemBars()) + controller.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE + } + } else { + @Suppress("DEPRECATION") + anchorView.systemUiVisibility = ( + View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hides NAVIGATION BAR; Android >= 4.0 + or View.SYSTEM_UI_FLAG_FULLSCREEN // hides STATUS BAR; Android >= 4.1 + or View.SYSTEM_UI_FLAG_IMMERSIVE // stays interactive; Android >= 4.4 + or View.SYSTEM_UI_FLAG_LAYOUT_STABLE // draw full window; Android >= 4.1 + or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN // draw full window; Android >= 4.1 + or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + ) + } + } + + private fun showSystemUI(anchorView: View) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + window.insetsController?.let { controller -> + controller.show(WindowInsets.Type.systemBars()) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + controller.systemBarsBehavior = WindowInsetsController.BEHAVIOR_DEFAULT + } + } + } else { + @Suppress("DEPRECATION") + anchorView.systemUiVisibility = ( + View.SYSTEM_UI_FLAG_LAYOUT_STABLE // draw full window; Android >= 4.1 + or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN // draw full window; Android >= 4.1 + or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION + ) + } + } + + companion object { + val TAG: String = PreviewImageActivity::class.java.simpleName + const val EXTRA_VIRTUAL_TYPE: String = "EXTRA_VIRTUAL_TYPE" + private const val KEY_WAITING_FOR_BINDER = "WAITING_FOR_BINDER" + private const val KEY_SYSTEM_VISIBLE = "TRUE" + + fun previewFileIntent(context: Context?, user: User?, file: OCFile?): Intent { + return Intent(context, PreviewImageActivity::class.java).apply { + putExtra(EXTRA_FILE, file) + putExtra(EXTRA_USER, user) + } + } + } +} diff --git a/build.gradle b/build.gradle index 75f92848c6..36329a5e41 100644 --- a/build.gradle +++ b/build.gradle @@ -11,7 +11,7 @@ buildscript { ext { androidLibraryVersion ="451cddeba122ff4d17dc8f88feb2fd7589f77567" - androidPluginVersion = '8.4.0' + androidPluginVersion = '8.5.0' androidxMediaVersion = '1.3.1' androidxTestVersion = "1.5.0" appCompatVersion = '1.7.0' diff --git a/gradle/verification-keyring.keys b/gradle/verification-keyring.keys index 7b6ef2d21b..80a501c48b 100644 --- a/gradle/verification-keyring.keys +++ b/gradle/verification-keyring.keys @@ -4457,6 +4457,43 @@ HBhD37R+PsUfSEPep+pmyBlX2nrKxxoRNsl5KWNiQw== =Phg6 -----END PGP PUBLIC KEY BLOCK----- +pub 1669C4BB543E0445 +uid Emily Johnston + +sub 5F6BA89D4B0869B9 +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: BCPG v1.68 + +mQGNBF3TQCcBDAD177B+Btl8XBEkBQ5jFSezFrpEl4arwCEa7htCp6T3h55HvYwz +P7Y9zWYXfhAC8XJlPQJYpqaQiiYtdlmOrOS4wbp5Lr+z/0XpFlJFzdKglxKYcdfP +ntnGyoj7Dz03v+SitL9Ct1YZmOGz6onlifXsCTkWraSJTqR6/y2dL0Beu7dLZp94 +fgf+FAfr77bwGhUhOh0pPI6ZK2VwNMiQN92jS/RYb6i7QjzO654ALTBR6R2sqx26 +C0NNsTUZ1WawPreT/rmR4vux1pvgbC8DcXqdptVb+iQPymnysEr69J0f7YC579+7 +itFIh6efV75W9nDqp9QB/1G808oYx1rglUstOCI0axSgSNyazbInW9qOI58rLQ4v +wnCSTWvesVNq+uO6aVrfpXIO3uUTI3t4mpBZgVYZ+g30BlCPRx52YofvQzYbbk9d +wCMUDQAzKGJi+mazkgBhcz+neEuNUlR/0fBMObzb7cAT4gGo/sSzYVNN5oT3u/Mi +J4hfzYUTFMsJBp0AEQEAAbQnRW1pbHkgSm9obnN0b24gPGVwbWpvaG5zdG9uQGdv +b2dsZS5jb20+uQGNBF3TQCcBDADJ17PQ4z5UVGBVUefEkTXeVlGX4oc+vUOVn8Z9 +B34sQkFmgsORuwm+/1rGthrMatro1Jka4UXSxYFMCt6XBz4/OdJbquxs6D85iuda +Id9aozCOJypkfprp+ez2PK7pWeOq6DQ/lqqNGyoHxA703wshI38sNcKRcypn/vf1 +VJlO3ZjfLCVDQw4+yNrdWiwklb4QS0xwjK9Bw9m8g6HiQGil8V/kL9Avpa76rGqa +P9YnkTo7NQG0cwEbpcxNUrkO7fFKjTOMTmfPkh5pHAHVS+FsrCsxhRs6Eb6u/qes +tHjpYj6qtMJ5V7oXeUI3OZ9nAPctSxoNVn6f0otsaatoBwcJNNhO/6ZIFH7NlgYQ +NN3a5Pz6NrU13+zMGbsxYzlL4/nVwhdn/kHPQ2tl4e2R79da7ModAjeafimikA3n +GZj1Zvo1iXKLfguM+U90kTrPKSxcJqwgV4QvrO9Z9llRTzhdBmrFePFhlxN3JdpS +KYXFInq6JwncmOAeIDhNYYcVXhcAEQEAAYkBvAQYAQoAJhYhBHYVrVYUTfI3b0nZ +ixZpxLtUPgRFBQJd00AnAhsMBQkDwmcAAAoJEBZpxLtUPgRF32gMAJoI+6dvnT7G +OJB4S0HAB2qhizmQ5MWiO7QE1HKQ6ShylihJfAIMnAMlLPhorr1ITZXaNMFO+rWN +O76BdsBxAkd0rKIIjMTU5r1HuS+XCGFzitffkJ2TgQ2K4vKnSgEpCsBilgCJzdJe +vrYoCAudkZAaeBcT0fsTtQDnWHUJSkyWro0ovaaPF5tJzMkFZQBlaNyb+DmWPyNt +5TP6iORnmeLNE0OajrUawFUcLUITdutn2t/PRE9LBDSlewE9Gabv79z8ZGAw7jPK +x/p+ePpIj6J05TM0BR9KLrk3avMIK+eKwcvm+nCyYA1jKr7c9E4bg+6sRbc6igvV +L4QeKzjeGll3vjifmghVXVKVBOW1fk10cMNKaTEkGng8OfLhJDQDXuNz4m8/pMA/ +wyGfH3HBGSx+F3GZUb00kU0HbxV2Vt0QlyjwT9vJfVGQ4Y+GhnMF6qErtdX8cBIW +FJXLEW77wWHN+QpRD6BShYZDvUA2mtO0zlB8reU+VCxgnFfm66DkUg== +=jYDe +-----END PGP PUBLIC KEY BLOCK----- + pub 16F71868F1D3A389 uid Lukhnos Liu diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 403b85a2f3..b612c63279 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -8,9 +8,6 @@ - - - @@ -81,7 +78,10 @@ - + + + + @@ -241,11 +241,13 @@ + + @@ -1146,6 +1148,11 @@ + + + + + @@ -3941,6 +3948,14 @@ + + + + + + + + diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e7646dead0..a4413138c9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a4269..b740cf1339 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/.