Skip to content

Commit 31b58d0

Browse files
ToS
Signed-off-by: tobiasKaminsky <[email protected]>
1 parent a4038fe commit 31b58d0

File tree

7 files changed

+222
-4
lines changed

7 files changed

+222
-4
lines changed

app/src/main/java/com/nextcloud/client/di/ComponentsModule.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import com.nextcloud.ui.SetStatusDialogFragment;
3434
import com.nextcloud.ui.composeActivity.ComposeActivity;
3535
import com.nextcloud.ui.fileactions.FileActionsBottomSheet;
36+
import com.nextcloud.ui.trashbinFileActions.TrashbinFileActionsBottomSheet;
3637
import com.nmc.android.ui.LauncherActivity;
3738
import com.owncloud.android.MainApp;
3839
import com.owncloud.android.authentication.AuthenticatorActivity;
@@ -97,6 +98,7 @@
9798
import com.owncloud.android.ui.dialog.StoragePermissionDialogFragment;
9899
import com.owncloud.android.ui.dialog.SyncFileNotEnoughSpaceDialogFragment;
99100
import com.owncloud.android.ui.dialog.SyncedFolderPreferencesDialogFragment;
101+
import com.owncloud.android.ui.dialog.TermsOfServiceDialog;
100102
import com.owncloud.android.ui.dialog.setupEncryption.SetupEncryptionDialogFragment;
101103
import com.owncloud.android.ui.fragment.ExtendedListFragment;
102104
import com.owncloud.android.ui.fragment.FeatureFragment;
@@ -124,7 +126,6 @@
124126
import com.owncloud.android.ui.preview.PreviewTextFragment;
125127
import com.owncloud.android.ui.preview.PreviewTextStringFragment;
126128
import com.owncloud.android.ui.preview.pdf.PreviewPdfFragment;
127-
import com.nextcloud.ui.trashbinFileActions.TrashbinFileActionsBottomSheet;
128129
import com.owncloud.android.ui.trashbin.TrashbinActivity;
129130

130131
import androidx.annotation.OptIn;
@@ -502,4 +503,6 @@ abstract class ComponentsModule {
502503
@ContributesAndroidInjector
503504
abstract BackgroundPlayerService backgroundPlayerService();
504505

506+
@ContributesAndroidInjector
507+
abstract TermsOfServiceDialog termsOfServiceDialog();
505508
}

app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java

+15-2
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@
108108
import com.owncloud.android.ui.dialog.SendShareDialog;
109109
import com.owncloud.android.ui.dialog.SortingOrderDialogFragment;
110110
import com.owncloud.android.ui.dialog.StoragePermissionDialogFragment;
111+
import com.owncloud.android.ui.dialog.TermsOfServiceDialog;
111112
import com.owncloud.android.ui.events.SearchEvent;
112113
import com.owncloud.android.ui.events.SyncEventFinished;
113114
import com.owncloud.android.ui.events.TokenPushEvent;
@@ -153,6 +154,7 @@
153154
import javax.inject.Inject;
154155

155156
import androidx.annotation.NonNull;
157+
import androidx.annotation.Nullable;
156158
import androidx.annotation.VisibleForTesting;
157159
import androidx.appcompat.app.AlertDialog;
158160
import androidx.appcompat.widget.SearchView;
@@ -202,6 +204,7 @@ public class FileDisplayActivity extends FileActivity
202204
private static final String KEY_WAITING_TO_PREVIEW = "WAITING_TO_PREVIEW";
203205
private static final String KEY_SYNC_IN_PROGRESS = "SYNC_IN_PROGRESS";
204206
private static final String KEY_WAITING_TO_SEND = "WAITING_TO_SEND";
207+
private static final String DIALOG_TAG_SHOW_TOS = "DIALOG_TAG_SHOW_TOS";
205208

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

@@ -773,11 +776,11 @@ public void showFileActions(OCFile file) {
773776
listOfFiles.onOverflowIconClicked(file, null);
774777
}
775778

776-
public @androidx.annotation.Nullable Fragment getLeftFragment() {
779+
public @Nullable Fragment getLeftFragment() {
777780
return getSupportFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES);
778781
}
779782

780-
public @androidx.annotation.Nullable
783+
public @Nullable
781784
@Deprecated OCFileListFragment getListOfFilesFragment() {
782785
Fragment listOfFiles = getSupportFragmentManager().findFragmentByTag(FileDisplayActivity.TAG_LIST_OF_FILES);
783786
if (listOfFiles instanceof OCFileListFragment) {
@@ -1433,6 +1436,11 @@ public void onReceive(Context context, Intent intent) {
14331436
case HOST_NOT_AVAILABLE:
14341437
showInfoBox(R.string.host_not_available);
14351438
break;
1439+
1440+
case SIGNING_TOS_NEEDED:
1441+
showTermsOfServiceDialog();
1442+
1443+
break;
14361444

14371445
default:
14381446
// nothing to do
@@ -1477,6 +1485,11 @@ public void onReceive(Context context, Intent intent) {
14771485
}
14781486
}
14791487
}
1488+
private void showTermsOfServiceDialog() {
1489+
if (getSupportFragmentManager().findFragmentByTag(DIALOG_TAG_SHOW_TOS) == null) {
1490+
new TermsOfServiceDialog().show(getSupportFragmentManager(), DIALOG_TAG_SHOW_TOS);
1491+
}
1492+
}
14801493

14811494
private boolean checkForRemoteOperationError(RemoteOperationResult syncResult) {
14821495
return ResultCode.UNAUTHORIZED == syncResult.getCode() || (syncResult.isException() && syncResult.getException() instanceof AuthenticatorException);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/*
2+
* Nextcloud - Android Client
3+
*
4+
* SPDX-FileCopyrightText: 2025 Tobias Kaminsky <[email protected]>
5+
* SPDX-License-Identifier: AGPL-3.0-or-later
6+
*/
7+
8+
package com.owncloud.android.ui.dialog
9+
10+
import android.app.Dialog
11+
import android.os.Bundle
12+
import android.view.View
13+
import android.widget.AdapterView
14+
import android.widget.ArrayAdapter
15+
import androidx.core.app.ActivityCompat.finishAffinity
16+
import androidx.fragment.app.DialogFragment
17+
import com.google.android.material.dialog.MaterialAlertDialogBuilder
18+
import com.nextcloud.android.lib.resources.tos.GetTermsRemoteOperation
19+
import com.nextcloud.android.lib.resources.tos.SignTermRemoteOperation
20+
import com.nextcloud.android.lib.resources.tos.Term
21+
import com.nextcloud.client.account.UserAccountManager
22+
import com.nextcloud.client.di.Injectable
23+
import com.nextcloud.client.network.ClientFactory
24+
import com.nextcloud.common.NextcloudClient
25+
import com.nextcloud.utils.extensions.setHtmlContent
26+
import com.owncloud.android.R
27+
import com.owncloud.android.databinding.DialogShowTosBinding
28+
import com.owncloud.android.lib.common.operations.RemoteOperationResult
29+
import com.owncloud.android.lib.common.utils.Log_OC
30+
import com.owncloud.android.utils.DisplayUtils
31+
import com.owncloud.android.utils.theme.ViewThemeUtils
32+
import kotlinx.coroutines.CoroutineScope
33+
import kotlinx.coroutines.Dispatchers
34+
import kotlinx.coroutines.launch
35+
import kotlinx.coroutines.withContext
36+
import javax.inject.Inject
37+
38+
class TermsOfServiceDialog : DialogFragment(), Injectable {
39+
private lateinit var binding: DialogShowTosBinding
40+
41+
@Inject
42+
lateinit var clientFactory: ClientFactory
43+
44+
@Inject
45+
lateinit var accountManager: UserAccountManager
46+
47+
@Inject
48+
lateinit var viewThemeUtils: ViewThemeUtils
49+
50+
lateinit var client: NextcloudClient
51+
lateinit var terms: List<Term>
52+
lateinit var languages: Map<String, String>
53+
54+
override fun onCreate(savedInstanceState: Bundle?) {
55+
super.onCreate(savedInstanceState)
56+
57+
fetchTerms()
58+
}
59+
60+
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
61+
binding = DialogShowTosBinding.inflate(requireActivity().layoutInflater)
62+
63+
return createDialogBuilder().create()
64+
}
65+
66+
private fun updateDialog() {
67+
binding.message.setHtmlContent(terms[0].renderedBody)
68+
69+
val arrayAdapter: ArrayAdapter<String> = ArrayAdapter(
70+
binding.root.context,
71+
android.R.layout.simple_spinner_item
72+
)
73+
74+
for ((_, _, languageCode) in terms) {
75+
arrayAdapter.add(languages[languageCode])
76+
}
77+
78+
arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
79+
80+
binding.languageDropdown.adapter = arrayAdapter
81+
binding.languageDropdown.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
82+
override fun onItemSelected(adapterView: AdapterView<*>?, view: View, position: Int, l: Long) {
83+
binding.message
84+
.setHtmlContent(terms[position].renderedBody)
85+
}
86+
87+
override fun onNothingSelected(adapterView: AdapterView<*>?) = Unit
88+
}
89+
90+
if (terms.size == 1) {
91+
binding.languageDropdown.visibility = View.GONE
92+
}
93+
}
94+
95+
private fun fetchTerms() {
96+
// TODO use viewLifecycleOwner instead
97+
CoroutineScope(Dispatchers.IO).launch {
98+
try {
99+
client = clientFactory.createNextcloudClient(accountManager.getUser())
100+
val result = GetTermsRemoteOperation().execute(client)
101+
102+
if (result.isSuccess &&
103+
!result.resultData.hasSigned &&
104+
result.resultData.terms.isNotEmpty()
105+
) {
106+
languages = result.resultData.languages
107+
terms = result.resultData.terms
108+
109+
withContext(Dispatchers.Main) {
110+
updateDialog()
111+
}
112+
}
113+
} catch (exception: ClientFactory.CreationException) {
114+
Log_OC.e(TAG, "Error creating client!")
115+
}
116+
}
117+
}
118+
119+
private fun createDialogBuilder(): MaterialAlertDialogBuilder {
120+
return MaterialAlertDialogBuilder(binding.root.context)
121+
.setView(binding.root)
122+
.setTitle(R.string.terms_of_service_title)
123+
.setNegativeButton(R.string.dialog_close) { _, _ ->
124+
activity?.let { finishAffinity(it) }
125+
}
126+
.setPositiveButton(R.string.terms_of_services_agree) { dialog, _ ->
127+
dialog.dismiss()
128+
Thread {
129+
val id = binding.languageDropdown.selectedItemPosition
130+
val signResult: RemoteOperationResult<Void> =
131+
SignTermRemoteOperation(terms.get(id).id).execute(client)
132+
if (!signResult.isSuccess) {
133+
DisplayUtils.showSnackMessage(view, R.string.sign_tos_failed)
134+
}
135+
}.start()
136+
}
137+
}
138+
139+
companion object {
140+
private const val TAG = "TermsOfServiceDialog"
141+
}
142+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?xml version="1.0" encoding="utf-8"?><!--
2+
~ Nextcloud - Android Client
3+
~
4+
~ SPDX-FileCopyrightText: 2022 Álvaro Brey <[email protected]>
5+
~ SPDX-FileCopyrightText: 2022 Nextcloud GmbH
6+
~ SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only
7+
-->
8+
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
9+
android:layout_width="match_parent"
10+
android:layout_height="match_parent"
11+
android:clickable="false"
12+
android:focusable="true"
13+
android:orientation="vertical"
14+
android:paddingHorizontal="?dialogPreferredPadding">
15+
16+
<Spinner
17+
android:id="@+id/language_dropdown"
18+
android:layout_width="wrap_content"
19+
android:layout_height="wrap_content"
20+
android:layout_gravity="end" />
21+
22+
<ScrollView
23+
android:layout_width="match_parent"
24+
android:layout_height="match_parent">
25+
26+
<LinearLayout
27+
android:layout_width="match_parent"
28+
android:layout_height="wrap_content"
29+
android:orientation="vertical">
30+
31+
<TextView
32+
android:id="@+id/message"
33+
android:layout_width="match_parent"
34+
android:layout_height="match_parent" />
35+
</LinearLayout>
36+
</ScrollView>
37+
</LinearLayout>

app/src/main/res/values/strings.xml

+4
Original file line numberDiff line numberDiff line change
@@ -1310,4 +1310,8 @@
13101310
<item quantity="one">%1$d download remaining</item>
13111311
<item quantity="other">%1$d downloads remaining</item>
13121312
</plurals>
1313+
<string name="sign_tos_needed">Please sign ToS</string>
1314+
<string name="sign_tos_failed">Please manually check terms of service!</string>
1315+
<string name="terms_of_service_title">Terms of service</string>
1316+
<string name="terms_of_services_agree">I agree to the above ToS</string>
13131317
</resources>

build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
*/
1111
buildscript {
1212
ext {
13-
androidLibraryVersion ="a885642fdd45c076c303e86eea33c7a7b1e91858"
13+
androidLibraryVersion = "8f6d922b05437166ba187e2f68c5db3a29bc0727"
1414
androidPluginVersion = '8.8.0'
1515
androidxMediaVersion = '1.5.1'
1616
androidxTestVersion = "1.6.1"

gradle/verification-metadata.xml

+19
Original file line numberDiff line numberDiff line change
@@ -9241,6 +9241,14 @@
92419241
<sha256 value="1bf1482df26848a59743a94ea6ee53d4ef6f014af5a21274214ab62eb2c6a8d6" origin="Generated by Gradle" reason="Artifact is not signed"/>
92429242
</artifact>
92439243
</component>
9244+
<component group="com.github.nextcloud" name="android-library" version="5fe34e4a90fc49612ffd94411b5b39f97235c992">
9245+
<artifact name="android-library-5fe34e4a90fc49612ffd94411b5b39f97235c992.aar">
9246+
<sha256 value="ebc753d2b655bd0fb46a89dc2e359ec9eb6e8e790fd77376cb4667762efc2d1e" origin="Generated by Gradle" reason="Artifact is not signed"/>
9247+
</artifact>
9248+
<artifact name="android-library-5fe34e4a90fc49612ffd94411b5b39f97235c992.module">
9249+
<sha256 value="5ed87fb9c467d68e9f6edf716bf4142f52a360b888026676667d25cfca7f2d79" origin="Generated by Gradle" reason="Artifact is not signed"/>
9250+
</artifact>
9251+
</component>
92449252
<component group="com.github.nextcloud" name="android-library" version="638326e14bc234ca3285852af461d41e6e08d1aa">
92459253
<artifact name="android-library-638326e14bc234ca3285852af461d41e6e08d1aa.aar">
92469254
<sha256 value="7026ad9ce3b66726f71b97acd19b6aa104ab38cf0b072ccd624900878e5e48bf" origin="Generated by Gradle" reason="Artifact is not signed"/>
@@ -9321,6 +9329,17 @@
93219329
<sha256 value="8311ad1fd1507e6184ff6a9f019ad13dc03075ae467936e1540c43993fd18ed4" origin="Generated by Gradle" reason="Artifact is not signed"/>
93229330
</artifact>
93239331
</component>
9332+
<component group="com.github.nextcloud" name="android-library"
9333+
version="8f6d922b05437166ba187e2f68c5db3a29bc0727">
9334+
<artifact name="android-library-8f6d922b05437166ba187e2f68c5db3a29bc0727.aar">
9335+
<sha256 value="c6dbef694d7c4a3264f38b860afaf2a7cb8be2ed063b53741d3ab7a375051b6d"
9336+
origin="Generated by Gradle" reason="Artifact is not signed" />
9337+
</artifact>
9338+
<artifact name="android-library-8f6d922b05437166ba187e2f68c5db3a29bc0727.module">
9339+
<sha256 value="ed3adfc1e8120360bdaa26c4d0337c54d288cbb2ca215cf07c38d034ae853fcc"
9340+
origin="Generated by Gradle" reason="Artifact is not signed" />
9341+
</artifact>
9342+
</component>
93249343
<component group="com.github.nextcloud" name="android-library" version="9fdcd0af0ff910086281f32e3b8ef74490671149">
93259344
<artifact name="android-library-9fdcd0af0ff910086281f32e3b8ef74490671149.aar">
93269345
<sha256 value="57ab4fd7c922875a7e0b5feac20aa27ab5df0fd3b4e042f92ed727c0b6316e81" origin="Generated by Gradle" reason="Artifact is not signed"/>

0 commit comments

Comments
 (0)