Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[stable-3.31] ToS #14511

Open
wants to merge 2 commits into
base: stable-3.31
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import com.nextcloud.ui.SetStatusDialogFragment;
import com.nextcloud.ui.composeActivity.ComposeActivity;
import com.nextcloud.ui.fileactions.FileActionsBottomSheet;
import com.nextcloud.ui.trashbinFileActions.TrashbinFileActionsBottomSheet;
import com.nmc.android.ui.LauncherActivity;
import com.owncloud.android.MainApp;
import com.owncloud.android.authentication.AuthenticatorActivity;
Expand Down Expand Up @@ -97,6 +98,7 @@
import com.owncloud.android.ui.dialog.StoragePermissionDialogFragment;
import com.owncloud.android.ui.dialog.SyncFileNotEnoughSpaceDialogFragment;
import com.owncloud.android.ui.dialog.SyncedFolderPreferencesDialogFragment;
import com.owncloud.android.ui.dialog.TermsOfServiceDialog;
import com.owncloud.android.ui.dialog.setupEncryption.SetupEncryptionDialogFragment;
import com.owncloud.android.ui.fragment.ExtendedListFragment;
import com.owncloud.android.ui.fragment.FeatureFragment;
Expand Down Expand Up @@ -124,7 +126,6 @@
import com.owncloud.android.ui.preview.PreviewTextFragment;
import com.owncloud.android.ui.preview.PreviewTextStringFragment;
import com.owncloud.android.ui.preview.pdf.PreviewPdfFragment;
import com.nextcloud.ui.trashbinFileActions.TrashbinFileActionsBottomSheet;
import com.owncloud.android.ui.trashbin.TrashbinActivity;

import androidx.annotation.OptIn;
Expand Down Expand Up @@ -502,4 +503,6 @@ abstract class ComponentsModule {
@ContributesAndroidInjector
abstract BackgroundPlayerService backgroundPlayerService();

@ContributesAndroidInjector
abstract TermsOfServiceDialog termsOfServiceDialog();
}
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@
import com.owncloud.android.ui.dialog.SendShareDialog;
import com.owncloud.android.ui.dialog.SortingOrderDialogFragment;
import com.owncloud.android.ui.dialog.StoragePermissionDialogFragment;
import com.owncloud.android.ui.dialog.TermsOfServiceDialog;
import com.owncloud.android.ui.events.SearchEvent;
import com.owncloud.android.ui.events.SyncEventFinished;
import com.owncloud.android.ui.events.TokenPushEvent;
Expand Down Expand Up @@ -153,6 +154,7 @@
import javax.inject.Inject;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.SearchView;
Expand Down Expand Up @@ -202,6 +204,7 @@ public class FileDisplayActivity extends FileActivity
private static final String KEY_WAITING_TO_PREVIEW = "WAITING_TO_PREVIEW";
private static final String KEY_SYNC_IN_PROGRESS = "SYNC_IN_PROGRESS";
private static final String KEY_WAITING_TO_SEND = "WAITING_TO_SEND";
private static final String DIALOG_TAG_SHOW_TOS = "DIALOG_TAG_SHOW_TOS";

public static final String ACTION_DETAILS = "com.owncloud.android.ui.activity.action.DETAILS";

Expand Down Expand Up @@ -773,11 +776,11 @@ public void showFileActions(OCFile file) {
listOfFiles.onOverflowIconClicked(file, null);
}

public @androidx.annotation.Nullable Fragment getLeftFragment() {
public @Nullable Fragment getLeftFragment() {
return getSupportFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES);
}

public @androidx.annotation.Nullable
public @Nullable
@Deprecated OCFileListFragment getListOfFilesFragment() {
Fragment listOfFiles = getSupportFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES);
if (listOfFiles instanceof OCFileListFragment) {
Expand Down Expand Up @@ -1433,6 +1436,11 @@ public void onReceive(Context context, Intent intent) {
case HOST_NOT_AVAILABLE:
showInfoBox(R.string.host_not_available);
break;

case SIGNING_TOS_NEEDED:
showTermsOfServiceDialog();

break;

default:
// nothing to do
Expand Down Expand Up @@ -1477,6 +1485,11 @@ public void onReceive(Context context, Intent intent) {
}
}
}
private void showTermsOfServiceDialog() {
if (getSupportFragmentManager().findFragmentByTag(DIALOG_TAG_SHOW_TOS) == null) {
new TermsOfServiceDialog().show(getSupportFragmentManager(), DIALOG_TAG_SHOW_TOS);
}
}

private boolean checkForRemoteOperationError(RemoteOperationResult syncResult) {
return ResultCode.UNAUTHORIZED == syncResult.getCode() || (syncResult.isException() && syncResult.getException() instanceof AuthenticatorException);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2025 Tobias Kaminsky <[email protected]>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

package com.owncloud.android.ui.dialog

import android.app.Dialog
import android.os.Bundle
import android.view.View
import android.widget.AdapterView
import android.widget.ArrayAdapter
import androidx.core.app.ActivityCompat.finishAffinity
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.lifecycleScope
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.nextcloud.android.lib.resources.tos.GetTermsRemoteOperation
import com.nextcloud.android.lib.resources.tos.SignTermRemoteOperation
import com.nextcloud.android.lib.resources.tos.Term
import com.nextcloud.client.account.UserAccountManager
import com.nextcloud.client.di.Injectable
import com.nextcloud.client.network.ClientFactory
import com.nextcloud.common.NextcloudClient
import com.nextcloud.utils.extensions.setHtmlContent
import com.owncloud.android.R
import com.owncloud.android.databinding.DialogShowTosBinding
import com.owncloud.android.lib.common.operations.RemoteOperationResult
import com.owncloud.android.lib.common.utils.Log_OC
import com.owncloud.android.utils.DisplayUtils
import com.owncloud.android.utils.theme.ViewThemeUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import javax.inject.Inject

class TermsOfServiceDialog : DialogFragment(), Injectable {
private lateinit var binding: DialogShowTosBinding

@Inject
lateinit var clientFactory: ClientFactory

@Inject
lateinit var accountManager: UserAccountManager

@Inject
lateinit var viewThemeUtils: ViewThemeUtils

lateinit var client: NextcloudClient
lateinit var terms: List<Term>
lateinit var languages: Map<String, String>

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
binding = DialogShowTosBinding.inflate(requireActivity().layoutInflater)
return createDialogBuilder().create()
}

override fun onStart() {
super.onStart()
fetchTerms()
}

private fun updateDialog() {
binding.message.setHtmlContent(terms[0].renderedBody)

val arrayAdapter: ArrayAdapter<String> = ArrayAdapter<String>(
binding.root.context,
android.R.layout.simple_spinner_item
).apply {
for ((_, _, languageCode) in terms) {
add(languages[languageCode])
}

setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
}

setupLanguageDropdown(arrayAdapter)
}

private fun setupLanguageDropdown(arrayAdapter: ArrayAdapter<String>) {
binding.languageDropdown.run {
adapter = arrayAdapter
onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(adapterView: AdapterView<*>?, view: View, position: Int, l: Long) {
binding.message
.setHtmlContent(terms[position].renderedBody)
}

override fun onNothingSelected(adapterView: AdapterView<*>?) = Unit
}

if (terms.size == 1) {
visibility = View.GONE
}
}
}

@Suppress("DEPRECATION")
private fun fetchTerms() {
lifecycleScope.launch(Dispatchers.IO) {
try {
client = clientFactory.createNextcloudClient(accountManager.user)
val result = GetTermsRemoteOperation().execute(client)

if (result.isSuccess &&
!result.resultData.hasSigned &&
result.resultData.terms.isNotEmpty()
) {
languages = result.resultData.languages
terms = result.resultData.terms

withContext(Dispatchers.Main) {
updateDialog()
}
}
} catch (exception: ClientFactory.CreationException) {
Log_OC.e(TAG, "Error creating client!")
}
}
}

private fun createDialogBuilder(): MaterialAlertDialogBuilder {
return MaterialAlertDialogBuilder(binding.root.context)
.setView(binding.root)
.setTitle(R.string.terms_of_service_title)
.setNegativeButton(R.string.dialog_close) { _, _ ->
activity?.let { finishAffinity(it) }
}
.setPositiveButton(R.string.terms_of_services_agree) { dialog, _ ->
dialog.dismiss()
agreeToS()
}
}

private fun agreeToS() {
lifecycleScope.launch(Dispatchers.IO) {
val id = binding.languageDropdown.selectedItemPosition
val signResult: RemoteOperationResult<Void> =
SignTermRemoteOperation(terms[id].id).execute(client)

if (!signResult.isSuccess) {
withContext(Dispatchers.Main) {
DisplayUtils.showSnackMessage(view, R.string.sign_tos_failed)
}
}
}
}

companion object {
private const val TAG = "TermsOfServiceDialog"
}
}
37 changes: 37 additions & 0 deletions app/src/main/res/layout/dialog_show_tos.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ Nextcloud - Android Client
~
~ SPDX-FileCopyrightText: 2022 Álvaro Brey <[email protected]>
~ SPDX-FileCopyrightText: 2022 Nextcloud GmbH
~ SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="false"
android:focusable="true"
android:orientation="vertical"
android:paddingHorizontal="?dialogPreferredPadding">

<Spinner
android:id="@+id/language_dropdown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end" />

<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">

<TextView
android:id="@+id/message"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</ScrollView>
</LinearLayout>
3 changes: 3 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1310,4 +1310,7 @@
<item quantity="one">%1$d download remaining</item>
<item quantity="other">%1$d downloads remaining</item>
</plurals>
<string name="sign_tos_failed">Please manually check terms of service!</string>
<string name="terms_of_service_title">Terms of service</string>
<string name="terms_of_services_agree">I agree to the above ToS</string>
</resources>
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
*/
buildscript {
ext {
androidLibraryVersion ="133cec38b0c1e536a36c45d362556a81de46f27c"
androidLibraryVersion = "24b0e624bbcbf6644c3cb64dafc5360f129625cd"
androidPluginVersion = '8.8.0'
androidxMediaVersion = '1.5.1'
androidxTestVersion = "1.6.1"
Expand Down
41 changes: 41 additions & 0 deletions gradle/verification-metadata.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9129,6 +9129,17 @@
<sha256 value="324ae2a4961a86e623e9112db4db616b589526834cf37a709ca932c865f4843b" origin="Generated by Gradle"/>
</artifact>
</component>
<component group="com.github.nextcloud" name="android-library"
version="24b0e624bbcbf6644c3cb64dafc5360f129625cd">
<artifact name="android-library-24b0e624bbcbf6644c3cb64dafc5360f129625cd.aar">
<sha256 value="0c92a494e651917d34b3f92915401c9fa3b114dccb3de4c07eab8c3a02af7cb0"
origin="Generated by Gradle" reason="Artifact is not signed" />
</artifact>
<artifact name="android-library-24b0e624bbcbf6644c3cb64dafc5360f129625cd.module">
<sha256 value="326276065ce28fc37039b9a7dffc8597962c2cb6499370723fd05bb6fd1d666c"
origin="Generated by Gradle" reason="Artifact is not signed" />
</artifact>
</component>
<component group="com.github.nextcloud" name="android-library" version="26dc8477962f12356db840bb1a774f0186b38e4d">
<artifact name="android-library-26dc8477962f12356db840bb1a774f0186b38e4d.aar">
<sha256 value="57ab4fd7c922875a7e0b5feac20aa27ab5df0fd3b4e042f92ed727c0b6316e81" origin="Generated by Gradle" reason="Artifact is not signed"/>
Expand Down Expand Up @@ -9249,6 +9260,14 @@
<sha256 value="1bf1482df26848a59743a94ea6ee53d4ef6f014af5a21274214ab62eb2c6a8d6" origin="Generated by Gradle" reason="Artifact is not signed"/>
</artifact>
</component>
<component group="com.github.nextcloud" name="android-library" version="5fe34e4a90fc49612ffd94411b5b39f97235c992">
<artifact name="android-library-5fe34e4a90fc49612ffd94411b5b39f97235c992.aar">
<sha256 value="ebc753d2b655bd0fb46a89dc2e359ec9eb6e8e790fd77376cb4667762efc2d1e" origin="Generated by Gradle" reason="Artifact is not signed"/>
</artifact>
<artifact name="android-library-5fe34e4a90fc49612ffd94411b5b39f97235c992.module">
<sha256 value="5ed87fb9c467d68e9f6edf716bf4142f52a360b888026676667d25cfca7f2d79" origin="Generated by Gradle" reason="Artifact is not signed"/>
</artifact>
</component>
<component group="com.github.nextcloud" name="android-library" version="638326e14bc234ca3285852af461d41e6e08d1aa">
<artifact name="android-library-638326e14bc234ca3285852af461d41e6e08d1aa.aar">
<sha256 value="7026ad9ce3b66726f71b97acd19b6aa104ab38cf0b072ccd624900878e5e48bf" origin="Generated by Gradle" reason="Artifact is not signed"/>
Expand Down Expand Up @@ -9329,6 +9348,17 @@
<sha256 value="8311ad1fd1507e6184ff6a9f019ad13dc03075ae467936e1540c43993fd18ed4" origin="Generated by Gradle" reason="Artifact is not signed"/>
</artifact>
</component>
<component group="com.github.nextcloud" name="android-library"
version="8f6d922b05437166ba187e2f68c5db3a29bc0727">
<artifact name="android-library-8f6d922b05437166ba187e2f68c5db3a29bc0727.aar">
<sha256 value="c6dbef694d7c4a3264f38b860afaf2a7cb8be2ed063b53741d3ab7a375051b6d"
origin="Generated by Gradle" reason="Artifact is not signed" />
</artifact>
<artifact name="android-library-8f6d922b05437166ba187e2f68c5db3a29bc0727.module">
<sha256 value="ed3adfc1e8120360bdaa26c4d0337c54d288cbb2ca215cf07c38d034ae853fcc"
origin="Generated by Gradle" reason="Artifact is not signed" />
</artifact>
</component>
<component group="com.github.nextcloud" name="android-library" version="9fdcd0af0ff910086281f32e3b8ef74490671149">
<artifact name="android-library-9fdcd0af0ff910086281f32e3b8ef74490671149.aar">
<sha256 value="57ab4fd7c922875a7e0b5feac20aa27ab5df0fd3b4e042f92ed727c0b6316e81" origin="Generated by Gradle" reason="Artifact is not signed"/>
Expand Down Expand Up @@ -9496,6 +9526,17 @@
<artifact name="android-library-fe415312da72f677c26aadffd2457521af9134c3.module">
<sha256 value="e6fd6827b2a1dfe697f4b84598633b28b52803c556948df00c2d0dec57a4bf35" origin="Generated by Gradle" reason="Artifact is not signed"/>
</artifact>
</component>
<component group="com.github.nextcloud" name="android-library"
version="fe8b18f5ae47889872a714ffdccbd67d7532d893">
<artifact name="android-library-fe8b18f5ae47889872a714ffdccbd67d7532d893.aar">
<sha256 value="bf19c118ffabd7298f526811bee45009c95596215e0d8f6bb08314e326e02732"
origin="Generated by Gradle" reason="Artifact is not signed" />
</artifact>
<artifact name="android-library-fe8b18f5ae47889872a714ffdccbd67d7532d893.module">
<sha256 value="aceba5c84a0a251ccf357c6758ce2bae107d1648f3efaa9679f992fbd6d039e3"
origin="Generated by Gradle" reason="Artifact is not signed" />
</artifact>
</component>
<component group="com.github.nextcloud-deps" name="qrcodescanner" version="0.1.2.4">
<artifact name="qrcodescanner-0.1.2.4.aar">
Expand Down
Loading