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

This commit is contained in:
Tobias Kaminsky 2024-06-26 03:39:12 +02:00
commit 9755f912f7
17 changed files with 609 additions and 566 deletions

View file

@ -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

View file

@ -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
...

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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) {

View file

@ -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)

View file

@ -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

View file

@ -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)
}
}

View file

@ -1,536 +0,0 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2020-2024 Andy Scherzinger <info@andy-scherzinger.de>
* SPDX-FileCopyrightText: 2023 Alper Ozturk <alper.ozturk@nextcloud.com>
* SPDX-FileCopyrightText: 2022 Álvaro Brey <alvaro@alvarobrey.com>
* SPDX-FileCopyrightText: 2019 Tobias Kaminsky <tobias@kaminsky.me>
* SPDX-FileCopyrightText: 2019 Chris Narkiewicz <hello@ezaquarii.com>
* SPDX-FileCopyrightText: 2016 ownCloud Inc.
* SPDX-FileCopyrightText: 2015 María Asensio Valverde <masensio@solidgear.es>
* SPDX-FileCopyrightText: 2013 David A. Velasco <dvelasco@solidgear.es>
* 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<User> 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.
* <p>
* 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> 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.
);
}
}

View file

@ -0,0 +1,527 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2020-2024 Andy Scherzinger <info@andy-scherzinger.de>
* SPDX-FileCopyrightText: 2023 Alper Ozturk <alper.ozturk@nextcloud.com>
* SPDX-FileCopyrightText: 2022 Álvaro Brey <alvaro@alvarobrey.com>
* SPDX-FileCopyrightText: 2019 Tobias Kaminsky <tobias@kaminsky.me>
* SPDX-FileCopyrightText: 2019 Chris Narkiewicz <hello@ezaquarii.com>
* SPDX-FileCopyrightText: 2016 ownCloud Inc.
* SPDX-FileCopyrightText: 2015 María Asensio Valverde <masensio@solidgear.es>
* SPDX-FileCopyrightText: 2013 David A. Velasco <dvelasco@solidgear.es>
* 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)
}
}
}
}

View file

@ -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'

View file

@ -4457,6 +4457,43 @@ HBhD37R+PsUfSEPep+pmyBlX2nrKxxoRNsl5KWNiQw==
=Phg6
-----END PGP PUBLIC KEY BLOCK-----
pub 1669C4BB543E0445
uid Emily Johnston <epmjohnston@google.com>
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 <lukhnos@lukhnos.org>

View file

@ -8,9 +8,6 @@
<trust group="com.fasterxml.jackson" name="jackson-base" version="2.14.2" reason="no keys available, no pom artifact to create a checksum"/>
<trust file=".*-sources[.]jar" regex="true"/>
</trusted-artifacts>
<ignored-keys>
<ignored-key id="1669C4BB543E0445" reason="Key couldn't be downloaded from any key server"/>
</ignored-keys>
<trusted-keys>
<trusted-key id="015479E1055341431B4545AB72475FD306B9CAB7" group="com.googlecode.javaewah" name="JavaEWAH" version="1.2.3"/>
<trusted-key id="042B29E928995B9DB963C636C7CA19B7B620D787" group="com.github.stephenc.jcip" name="jcip-annotations" version="1.0-1"/>
@ -81,7 +78,10 @@
<trusted-key id="2E52CCA3842FA13C188F1DB50B45DDD344B5FFD1" group="tools.fastlane" name="screengrab" version="2.1.1"/>
<trusted-key id="2E8191ADD82E5C6ABD8E24D802499A5DAEC7ADA9" group="^com[.]twelvemonkeys($|([.].*))" regex="true"/>
<trusted-key id="2E92113263FC31C74CCBAAB20E91C2DE43B72BB1" group="org.ec4j.core"/>
<trusted-key id="3051D45031E13516A6E8FAFF280D66A55F5316C5" group="org.bitbucket.b_c" name="jose4j" version="0.7.0"/>
<trusted-key id="3051D45031E13516A6E8FAFF280D66A55F5316C5">
<trusting group="org.bitbucket.b_c" name="jose4j" version="0.7.0"/>
<trusting group="org.bitbucket.b_c" name="jose4j" version="0.9.5"/>
</trusted-key>
<trusted-key id="31BAE2E51D95E0F8AD9B7BCC40A3C4432BD7308C" group="com.googlecode.juniversalchardet" name="juniversalchardet" version="1.0.3"/>
<trusted-key id="3241A3ABA33FF6F69B75E2DDEC9458EED5C5AB12" group="id.zelory" name="compressor" version="3.0.1"/>
<trusted-key id="324E460B9DB8F4515F3EAED9930C5B1EA41B1AA1" group="com.beust" name="jcommander" version="1.48"/>
@ -241,11 +241,13 @@
<trusting group="androidx.annotation"/>
<trusting group="androidx.appcompat"/>
<trusting group="androidx.core"/>
<trusting group="androidx.databinding"/>
<trusting group="androidx.fragment"/>
<trusting group="androidx.lifecycle"/>
<trusting group="androidx.transition" name="transition" version="1.5.0"/>
<trusting group="androidx.webkit" name="webkit" version="1.11.0"/>
<trusting group="^androidx[.]compose($|([.].*))" regex="true"/>
<trusting group="^com[.]android($|([.].*))" regex="true"/>
</trusted-key>
<trusted-key id="A6D6C97108B8585F91B158748671A8DF71296252" group="^com[.]squareup($|([.].*))" regex="true"/>
<trusted-key id="A7892505CF1A58076453E52D7999BEFBA1039E8B" group="net.bytebuddy"/>
@ -1146,6 +1148,11 @@
<sha256 value="b037fdfb267dc0141ab9f4e4e85daf87b175cf311248d54b501b58ec42345315" origin="Generated by Gradle" reason="Artifact is not signed"/>
</artifact>
</component>
<component group="androidx.core" name="core-ktx" version="1.6.0">
<artifact name="core-ktx-1.6.0.module">
<sha256 value="e937bf22c0da28e872300fc6b96029b83ff75aa8d0ab1ec039aae3d8af6513dd" origin="Generated by Gradle" reason="Artifact is not signed"/>
</artifact>
</component>
<component group="androidx.core" name="core-ktx" version="1.8.0">
<artifact name="core-ktx-1.8.0.module">
<sha256 value="a91bc3e02f209f643dd8275345a9e3003ce20d64fc0760eccf479c1709842f72" origin="Generated by Gradle" reason="Artifact is not signed"/>
@ -3941,6 +3948,14 @@
<sha256 value="745894f1d15599871b1c3a6cc09f0da03268f1373ce937ce35942b933d6518e6" origin="Generated by Gradle" reason="Artifact is not signed"/>
</artifact>
</component>
<component group="com.android.tools.build" name="bundletool" version="1.16.0">
<artifact name="bundletool-1.16.0.jar">
<sha256 value="1ea2bf5274bbac7a3bb5618521d2fa11fb04e900e33a8a646029ec6332fc08c8" origin="Generated by Gradle" reason="Artifact is not signed"/>
</artifact>
<artifact name="bundletool-1.16.0.pom">
<sha256 value="f2e8aad4455a42372462d5e18a26973d67571179a6757f3825f227e91af2f2db" origin="Generated by Gradle" reason="Artifact is not signed"/>
</artifact>
</component>
<component group="com.android.tools.build" name="gradle" version="8.2.2">
<artifact name="gradle-8.2.2.jar">
<sha256 value="ca50f47cd13d347de7c89ecb2bd06487b03a88687c46c2b776bc0a9323958955" origin="Generated by Gradle" reason="Artifact is not signed"/>

View file

@ -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

2
gradlew vendored
View file

@ -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/.