Skip to content

Commit

Permalink
Merge pull request nextcloud#13408 from nextcloud/feature/offline-cre…
Browse files Browse the repository at this point in the history
…ate-folder-operation

Feature - Create Folder When Device Don't Have Internet Connection
  • Loading branch information
AndyScherzinger authored Sep 4, 2024
2 parents 3f2c517 + b02e46a commit 8f42be4
Show file tree
Hide file tree
Showing 116 changed files with 3,355 additions and 488 deletions.
1,301 changes: 1,301 additions & 0 deletions app/schemas/com.nextcloud.client.database.NextcloudDatabase/84.json

Large diffs are not rendered by default.

6 changes: 6 additions & 0 deletions app/src/androidTest/java/com/owncloud/android/AbstractIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
import android.os.NetworkOnMainThreadException;
import android.text.TextUtils;
import android.view.View;

Expand Down Expand Up @@ -375,6 +376,11 @@ public void uploadFile(File file, String remotePath) {

public void uploadOCUpload(OCUpload ocUpload) {
ConnectivityService connectivityServiceMock = new ConnectivityService() {
@Override
public boolean isNetworkAndServerAvailable() throws NetworkOnMainThreadException {
return false;
}

@Override
public boolean isConnected() {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import android.content.ActivityNotFoundException;
import android.net.Uri;
import android.os.Bundle;
import android.os.NetworkOnMainThreadException;

import com.nextcloud.client.account.User;
import com.nextcloud.client.account.UserAccountManager;
Expand Down Expand Up @@ -187,6 +188,11 @@ public void uploadOCUpload(OCUpload ocUpload) {

public void uploadOCUpload(OCUpload ocUpload, int localBehaviour) {
ConnectivityService connectivityServiceMock = new ConnectivityService() {
@Override
public boolean isNetworkAndServerAvailable() throws NetworkOnMainThreadException {
return false;
}

@Override
public boolean isConnected() {
return false;
Expand Down
17 changes: 17 additions & 0 deletions app/src/androidTest/java/com/owncloud/android/UploadIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
*/
package com.owncloud.android;

import android.os.NetworkOnMainThreadException;

import com.nextcloud.client.account.UserAccountManagerImpl;
import com.nextcloud.client.device.BatteryStatus;
import com.nextcloud.client.device.PowerManagementService;
Expand Down Expand Up @@ -56,6 +58,11 @@ public class UploadIT extends AbstractOnServerIT {
targetContext.getContentResolver());

private ConnectivityService connectivityServiceMock = new ConnectivityService() {
@Override
public boolean isNetworkAndServerAvailable() throws NetworkOnMainThreadException {
return false;
}

@Override
public boolean isConnected() {
return false;
Expand Down Expand Up @@ -274,6 +281,11 @@ public BatteryStatus getBattery() {
@Test
public void testUploadOnWifiOnlyButNoWifi() {
ConnectivityService connectivityServiceMock = new ConnectivityService() {
@Override
public boolean isNetworkAndServerAvailable() throws NetworkOnMainThreadException {
return false;
}

@Override
public boolean isConnected() {
return false;
Expand Down Expand Up @@ -358,6 +370,11 @@ public void testUploadOnWifiOnlyAndWifi() {
@Test
public void testUploadOnWifiOnlyButMeteredWifi() {
ConnectivityService connectivityServiceMock = new ConnectivityService() {
@Override
public boolean isNetworkAndServerAvailable() throws NetworkOnMainThreadException {
return false;
}

@Override
public boolean isConnected() {
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ abstract class FileUploaderIT : AbstractOnServerIT() {
private var uploadsStorageManager: UploadsStorageManager? = null

private val connectivityServiceMock: ConnectivityService = object : ConnectivityService {
override fun isNetworkAndServerAvailable(): Boolean {
return false
}

override fun isConnected(): Boolean {
return false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ public void screenshotTextFiles() {

ConflictsResolveActivity sut = activityRule.launchActivity(intent);

ConflictsResolveDialog dialog = ConflictsResolveDialog.newInstance(existingFile,
ConflictsResolveDialog dialog = ConflictsResolveDialog.newInstance(targetContext,
existingFile,
newFile,
UserAccountManagerImpl
.fromContext(targetContext)
Expand Down Expand Up @@ -209,7 +210,7 @@ public void keepExisting() {

getInstrumentation().waitForIdleSync();

onView(withId(R.id.existing_checkbox)).perform(click());
onView(withId(R.id.right_checkbox)).perform(click());

DialogFragment dialog = (DialogFragment) sut.getSupportFragmentManager().findFragmentByTag("conflictDialog");
screenshot(Objects.requireNonNull(dialog.requireDialog().getWindow()).getDecorView());
Expand Down Expand Up @@ -255,7 +256,7 @@ public void keepNew() {

getInstrumentation().waitForIdleSync();

onView(withId(R.id.new_checkbox)).perform(click());
onView(withId(R.id.left_checkbox)).perform(click());

DialogFragment dialog = (DialogFragment) sut.getSupportFragmentManager().findFragmentByTag("conflictDialog");
screenshot(Objects.requireNonNull(dialog.requireDialog().getWindow()).getDecorView());
Expand Down Expand Up @@ -300,8 +301,8 @@ public void keepBoth() {

getInstrumentation().waitForIdleSync();

onView(withId(R.id.existing_checkbox)).perform(click());
onView(withId(R.id.new_checkbox)).perform(click());
onView(withId(R.id.right_checkbox)).perform(click());
onView(withId(R.id.left_checkbox)).perform(click());

DialogFragment dialog = (DialogFragment) sut.getSupportFragmentManager().findFragmentByTag("conflictDialog");
screenshot(Objects.requireNonNull(dialog.requireDialog().getWindow()).getDecorView());
Expand Down
4 changes: 4 additions & 0 deletions app/src/debug/java/com/nextcloud/test/TestActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ class TestActivity :
override fun getConnectivity(): Connectivity {
return Connectivity.CONNECTED_WIFI
}

override fun isNetworkAndServerAvailable(): Boolean {
return false
}
}

override fun onCreate(savedInstanceState: Bundle?) {
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,9 @@
</intent-filter>
</activity>

<receiver
android:name="com.nextcloud.receiver.OfflineOperationActionReceiver"
android:exported="false" />
<receiver
android:name="com.nextcloud.client.jobs.MediaFoldersDetectionWork$NotificationReceiver"
android:exported="false" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import android.content.Context
import com.nextcloud.client.core.Clock
import com.nextcloud.client.database.dao.ArbitraryDataDao
import com.nextcloud.client.database.dao.FileDao
import com.nextcloud.client.database.dao.OfflineOperationDao
import dagger.Module
import dagger.Provides
import javax.inject.Singleton
Expand All @@ -33,4 +34,9 @@ class DatabaseModule {
fun fileDao(nextcloudDatabase: NextcloudDatabase): FileDao {
return nextcloudDatabase.fileDao()
}

@Provides
fun offlineOperationsDao(nextcloudDatabase: NextcloudDatabase): OfflineOperationDao {
return nextcloudDatabase.offlineOperationDao()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@ import com.nextcloud.client.core.Clock
import com.nextcloud.client.core.ClockImpl
import com.nextcloud.client.database.dao.ArbitraryDataDao
import com.nextcloud.client.database.dao.FileDao
import com.nextcloud.client.database.dao.OfflineOperationDao
import com.nextcloud.client.database.entity.ArbitraryDataEntity
import com.nextcloud.client.database.entity.CapabilityEntity
import com.nextcloud.client.database.entity.ExternalLinkEntity
import com.nextcloud.client.database.entity.FileEntity
import com.nextcloud.client.database.entity.FilesystemEntity
import com.nextcloud.client.database.entity.OfflineOperationEntity
import com.nextcloud.client.database.entity.ShareEntity
import com.nextcloud.client.database.entity.SyncedFolderEntity
import com.nextcloud.client.database.entity.UploadEntity
Expand All @@ -41,7 +43,8 @@ import com.owncloud.android.db.ProviderMeta
ShareEntity::class,
SyncedFolderEntity::class,
UploadEntity::class,
VirtualEntity::class
VirtualEntity::class,
OfflineOperationEntity::class
],
version = ProviderMeta.DB_VERSION,
autoMigrations = [
Expand All @@ -61,7 +64,8 @@ import com.owncloud.android.db.ProviderMeta
AutoMigration(from = 79, to = 80),
AutoMigration(from = 80, to = 81),
AutoMigration(from = 81, to = 82),
AutoMigration(from = 82, to = 83)
AutoMigration(from = 82, to = 83),
AutoMigration(from = 83, to = 84)
],
exportSchema = true
)
Expand All @@ -70,6 +74,7 @@ abstract class NextcloudDatabase : RoomDatabase() {

abstract fun arbitraryDataDao(): ArbitraryDataDao
abstract fun fileDao(): FileDao
abstract fun offlineOperationDao(): OfflineOperationDao

companion object {
const val FIRST_ROOM_DB_VERSION = 65
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2024 Alper Ozturk <[email protected]>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

package com.nextcloud.client.database.dao

import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import com.nextcloud.client.database.entity.OfflineOperationEntity

@Dao
interface OfflineOperationDao {
@Query("SELECT * FROM offline_operations")
fun getAll(): List<OfflineOperationEntity>

@Insert
fun insert(vararg entity: OfflineOperationEntity)

@Update
fun update(entity: OfflineOperationEntity)

@Delete
fun delete(entity: OfflineOperationEntity)

@Query("DELETE FROM offline_operations WHERE offline_operations_path = :path")
fun deleteByPath(path: String)

@Query("SELECT * FROM offline_operations WHERE offline_operations_path = :path LIMIT 1")
fun getByPath(path: String): OfflineOperationEntity?

@Query("SELECT * FROM offline_operations WHERE offline_operations_parent_oc_file_id = :parentOCFileId")
fun getSubDirectoriesByParentOCFileId(parentOCFileId: Long): List<OfflineOperationEntity>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2024 Alper Ozturk <[email protected]>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

package com.nextcloud.client.database.entity

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import com.nextcloud.model.OfflineOperationType
import com.owncloud.android.db.ProviderMeta.ProviderTableMeta

@Entity(tableName = ProviderTableMeta.OFFLINE_OPERATION_TABLE_NAME)
data class OfflineOperationEntity(
@PrimaryKey(autoGenerate = true)
@ColumnInfo(name = ProviderTableMeta._ID)
val id: Int? = null,

@ColumnInfo(name = ProviderTableMeta.OFFLINE_OPERATION_PARENT_OC_FILE_ID)
var parentOCFileId: Long? = null,

@ColumnInfo(name = ProviderTableMeta.OFFLINE_OPERATION_PARENT_PATH)
var parentPath: String? = null,

@ColumnInfo(name = ProviderTableMeta.OFFLINE_OPERATION_TYPE)
var type: OfflineOperationType? = null,

@ColumnInfo(name = ProviderTableMeta.OFFLINE_OPERATION_PATH)
var path: String? = null,

@ColumnInfo(name = ProviderTableMeta.OFFLINE_OPERATION_FILE_NAME)
var filename: String? = null,

@ColumnInfo(name = ProviderTableMeta.OFFLINE_OPERATION_CREATED_AT)
var createdAt: Long? = null
)
6 changes: 6 additions & 0 deletions app/src/main/java/com/nextcloud/client/di/AppModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.nextcloud.client.core.ThreadPoolAsyncRunner;
import com.nextcloud.client.database.dao.ArbitraryDataDao;
import com.nextcloud.client.device.DeviceInfo;
import com.nextcloud.client.jobs.operation.FileOperationHelper;
import com.nextcloud.client.logger.FileLogHandler;
import com.nextcloud.client.logger.Logger;
import com.nextcloud.client.logger.LoggerImpl;
Expand Down Expand Up @@ -250,6 +251,11 @@ PassCodeManager passCodeManager(AppPreferences preferences, Clock clock) {
return new PassCodeManager(preferences, clock);
}

@Provides
FileOperationHelper fileOperationHelper(CurrentAccountProvider currentAccountProvider, Context context) {
return new FileOperationHelper(currentAccountProvider.getUser(), context, fileDataStorageManager(currentAccountProvider, context));
}

@Provides
@Singleton
UsersAndGroupsSearchConfig userAndGroupSearchConfig() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import com.nextcloud.client.widget.DashboardWidgetConfigurationActivity;
import com.nextcloud.client.widget.DashboardWidgetProvider;
import com.nextcloud.client.widget.DashboardWidgetService;
import com.nextcloud.receiver.NetworkChangeReceiver;
import com.nextcloud.ui.ChooseAccountDialogFragment;
import com.nextcloud.ui.ImageDetailFragment;
import com.nextcloud.ui.SetStatusDialogFragment;
Expand Down Expand Up @@ -313,6 +314,9 @@ abstract class ComponentsModule {
@ContributesAndroidInjector
abstract BootupBroadcastReceiver bootupBroadcastReceiver();

@ContributesAndroidInjector
abstract NetworkChangeReceiver networkChangeReceiver();

@ContributesAndroidInjector
abstract NotificationWork.NotificationReceiver notificationWorkBroadcastReceiver();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import com.nextcloud.client.documentscan.GeneratePDFUseCase
import com.nextcloud.client.documentscan.GeneratePdfFromImagesWork
import com.nextcloud.client.integrations.deck.DeckApi
import com.nextcloud.client.jobs.download.FileDownloadWorker
import com.nextcloud.client.jobs.offlineOperations.OfflineOperationsWorker
import com.nextcloud.client.jobs.upload.FileUploadWorker
import com.nextcloud.client.logger.Logger
import com.nextcloud.client.network.ConnectivityService
Expand Down Expand Up @@ -95,12 +96,17 @@ class BackgroundJobFactory @Inject constructor(
GeneratePdfFromImagesWork::class -> createPDFGenerateWork(context, workerParameters)
HealthStatusWork::class -> createHealthStatusWork(context, workerParameters)
TestJob::class -> createTestJob(context, workerParameters)
OfflineOperationsWorker::class -> createOfflineOperationsWorker(context, workerParameters)
InternalTwoWaySyncWork::class -> createInternalTwoWaySyncWork(context, workerParameters)
else -> null // caller falls back to default factory
}
}
}

private fun createOfflineOperationsWorker(context: Context, params: WorkerParameters): ListenableWorker {
return OfflineOperationsWorker(accountManager.user, context, connectivityService, viewThemeUtils.get(), params)
}

private fun createFilesExportWork(context: Context, params: WorkerParameters): ListenableWorker {
return FilesExportWork(
context,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,5 +168,7 @@ interface BackgroundJobManager {
fun schedulePeriodicHealthStatus()
fun startHealthStatus()
fun bothFilesSyncJobsRunning(syncedFolderID: Long): Boolean
fun startOfflineOperations()
fun startPeriodicallyOfflineOperation()
fun scheduleInternal2WaySync()
}
Loading

0 comments on commit 8f42be4

Please sign in to comment.