diff --git a/app/src/huawei/java/com/owncloud/android/ui/activity/HuaweiCommunityActivity.kt b/app/src/huawei/java/com/owncloud/android/ui/activity/HuaweiCommunityActivity.kt index bc3bf0e17f..03c453e8ca 100644 --- a/app/src/huawei/java/com/owncloud/android/ui/activity/HuaweiCommunityActivity.kt +++ b/app/src/huawei/java/com/owncloud/android/ui/activity/HuaweiCommunityActivity.kt @@ -20,14 +20,15 @@ */ package com.owncloud.android.ui.activity +import android.os.Bundle import android.view.View /** * Activity providing information about ways to participate in the app's development. */ class HuaweiCommunityActivity : CommunityActivity() { - override fun setupContent() { - super.setupContent() + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) binding.communityReleaseCandidatePlaystore.visibility = View.GONE } } diff --git a/app/src/main/java/com/nextcloud/utils/extensions/TextViewExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/TextViewExtensions.kt new file mode 100644 index 0000000000..52ad55653b --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/TextViewExtensions.kt @@ -0,0 +1,32 @@ +/* + * Nextcloud Android client application + * + * @author Alper Ozturk + * Copyright (C) 2023 Alper Ozturk + * Copyright (C) 2023 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.nextcloud.utils.extensions + +import android.text.method.LinkMovementMethod +import android.widget.TextView +import androidx.core.text.HtmlCompat + +@Suppress("NewLineAtEndOfFile") +fun TextView.setHtmlContent(value: String) { + movementMethod = LinkMovementMethod.getInstance() + text = HtmlCompat.fromHtml(value, HtmlCompat.FROM_HTML_MODE_LEGACY) +} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/CommunityActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/CommunityActivity.java deleted file mode 100644 index 05662f63b7..0000000000 --- a/app/src/main/java/com/owncloud/android/ui/activity/CommunityActivity.java +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Nextcloud Android client application - * - * @author Andy Scherzinger - * @author Tobias Kaminsky - * Copyright (C) 2016 Andy Scherzinger - * Copyright (C) 2016 Nextcloud - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this program. If not, see . - */ -package com.owncloud.android.ui.activity; - -import android.os.Bundle; -import android.text.Html; -import android.text.method.LinkMovementMethod; -import android.view.MenuItem; -import android.widget.TextView; - -import com.google.android.material.button.MaterialButton; -import com.owncloud.android.R; -import com.owncloud.android.databinding.CommunityLayoutBinding; -import com.owncloud.android.utils.DisplayUtils; - -/** - * Activity providing information about ways to participate in the app's development. - */ -public class CommunityActivity extends DrawerActivity { - - protected CommunityLayoutBinding binding; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - binding = CommunityLayoutBinding.inflate(getLayoutInflater()); - setContentView(binding.getRoot()); - - // setup toolbar - setupToolbar(); - - updateActionBarTitleAndHomeButtonByString(getString(R.string.drawer_community)); - - // setup drawer - setupDrawer(R.id.nav_community); - - setupContent(); - } - - protected void setupContent() { - binding.communityReleaseCandidateText.setMovementMethod(LinkMovementMethod.getInstance()); - - TextView contributeForumView = binding.communityContributeForumText; - contributeForumView.setMovementMethod(LinkMovementMethod.getInstance()); - contributeForumView.setText(Html.fromHtml(getString(R.string.community_contribute_forum_text) + " " + - getString(R.string.community_contribute_forum_text_link, - viewThemeUtils - .files - .primaryColorToHexString(this), - getString(R.string.help_link), - getString(R.string.community_contribute_forum_forum)))); - - TextView contributeTranslationView = binding.communityContributeTranslateText; - contributeTranslationView.setMovementMethod(LinkMovementMethod.getInstance()); - contributeTranslationView.setText(Html.fromHtml( - getString(R.string.community_contribute_translate_link, - viewThemeUtils.files.primaryColorToHexString(this), - getString(R.string.translation_link), - getString(R.string.community_contribute_translate_translate)) + " " + - getString(R.string.community_contribute_translate_text))); - - TextView contributeGithubView = binding.communityContributeGithubText; - contributeGithubView.setMovementMethod(LinkMovementMethod.getInstance()); - contributeGithubView.setText(Html.fromHtml( - getString(R.string.community_contribute_github_text, - getString(R.string.community_contribute_github_text_link, - viewThemeUtils.files.primaryColorToHexString(this), - getString(R.string.contributing_link))))); - - MaterialButton reportButton = binding.communityTestingReport; - viewThemeUtils.material.colorMaterialButtonPrimaryFilled(reportButton); - reportButton.setOnClickListener(v -> DisplayUtils.startLinkIntent(this, R.string.report_issue_empty_link)); - - binding.communityBetaFdroid.setOnClickListener( - l -> DisplayUtils.startLinkIntent(this, R.string.fdroid_beta_link)); - - binding.communityReleaseCandidateFdroid.setOnClickListener( - l -> DisplayUtils.startLinkIntent(this, R.string.fdroid_link)); - - binding.communityReleaseCandidatePlaystore.setOnClickListener( - l -> DisplayUtils.startLinkIntent(this, R.string.play_store_register_beta)); - - binding.communityBetaApk.setOnClickListener( - l -> DisplayUtils.startLinkIntent(this, R.string.beta_apk_link)); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - boolean retval = true; - if (item.getItemId() == android.R.id.home) { - if (isDrawerOpen()) { - closeDrawer(); - } else { - openDrawer(); - } - } else { - retval = super.onOptionsItemSelected(item); - } - return retval; - } - - @Override - protected void onResume() { - super.onResume(); - - setDrawerMenuItemChecked(R.id.nav_community); - } -} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/CommunityActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/CommunityActivity.kt new file mode 100644 index 0000000000..28dc94c67f --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/activity/CommunityActivity.kt @@ -0,0 +1,146 @@ +/* + * Nextcloud Android client application + * + * @author Andy Scherzinger + * @author Tobias Kaminsky + * Copyright (C) 2016 Andy Scherzinger + * Copyright (C) 2016 Nextcloud + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this program. If not, see . + */ +package com.owncloud.android.ui.activity + +import android.os.Bundle +import android.text.method.LinkMovementMethod +import android.view.MenuItem +import com.nextcloud.utils.extensions.setHtmlContent +import com.owncloud.android.R +import com.owncloud.android.databinding.CommunityLayoutBinding +import com.owncloud.android.utils.DisplayUtils + +/** + * Activity providing information about ways to participate in the app's development. + */ +open class CommunityActivity : DrawerActivity() { + lateinit var binding: CommunityLayoutBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = CommunityLayoutBinding.inflate(layoutInflater) + setContentView(binding.root) + + setupToolbar() + updateActionBarTitleAndHomeButtonByString(getString(R.string.drawer_community)) + + setupDrawer(R.id.nav_community) + binding.communityReleaseCandidateText.movementMethod = LinkMovementMethod.getInstance() + setupContributeForumView() + setupContributeTranslationView() + setupContributeGithubView() + setupReportButton() + setOnClickListeners() + } + + private fun setupContributeForumView() { + val htmlContent = getString(R.string.community_contribute_forum_text) + " " + + getString( + R.string.community_contribute_forum_text_link, + viewThemeUtils.files + .primaryColorToHexString(this), + getString(R.string.help_link), + getString(R.string.community_contribute_forum_forum) + ) + binding.communityContributeForumText.setHtmlContent(htmlContent) + } + + private fun setupContributeTranslationView() { + val htmlContent = getString( + R.string.community_contribute_translate_link, + viewThemeUtils.files.primaryColorToHexString(this), + getString(R.string.translation_link), + getString(R.string.community_contribute_translate_translate) + ) + " " + + getString(R.string.community_contribute_translate_text) + binding.communityContributeTranslateText.setHtmlContent(htmlContent) + } + + private fun setupContributeGithubView() { + val htmlContent = getString( + R.string.community_contribute_github_text, + getString( + R.string.community_contribute_github_text_link, + viewThemeUtils.files.primaryColorToHexString(this), + getString(R.string.contributing_link) + ) + ) + binding.communityContributeGithubText.setHtmlContent(htmlContent) + } + + private fun setupReportButton() { + val reportButton = binding.communityTestingReport + viewThemeUtils.material.colorMaterialButtonPrimaryFilled(reportButton) + reportButton.setOnClickListener { + DisplayUtils.startLinkIntent( + this, + R.string.report_issue_empty_link + ) + } + } + + private fun setOnClickListeners() { + binding.communityBetaFdroid.setOnClickListener { + DisplayUtils.startLinkIntent( + this, + R.string.fdroid_beta_link + ) + } + binding.communityReleaseCandidateFdroid.setOnClickListener { + DisplayUtils.startLinkIntent( + this, + R.string.fdroid_link + ) + } + binding.communityReleaseCandidatePlaystore.setOnClickListener { + DisplayUtils.startLinkIntent( + this, + R.string.play_store_register_beta + ) + } + binding.communityBetaApk.setOnClickListener { + DisplayUtils.startLinkIntent( + this, + R.string.beta_apk_link + ) + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + var retval = true + if (item.itemId == android.R.id.home) { + if (isDrawerOpen) { + closeDrawer() + } else { + openDrawer() + } + } else { + retval = super.onOptionsItemSelected(item) + } + return retval + } + + override fun onResume() { + super.onResume() + setDrawerMenuItemChecked(R.id.nav_community) + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FilePickerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FilePickerActivity.kt similarity index 55% rename from app/src/main/java/com/owncloud/android/ui/activity/FilePickerActivity.java rename to app/src/main/java/com/owncloud/android/ui/activity/FilePickerActivity.kt index 029bb13a82..7ed93b2e2b 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FilePickerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FilePickerActivity.kt @@ -18,40 +18,29 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +package com.owncloud.android.ui.activity -package com.owncloud.android.ui.activity; - -import android.os.Bundle; -import android.view.View; - -import com.owncloud.android.R; -import com.owncloud.android.ui.fragment.OCFileListFragment; - -import androidx.fragment.app.FragmentTransaction; +import android.os.Bundle +import com.owncloud.android.R +import com.owncloud.android.ui.fragment.OCFileListFragment /** * File picker of remote files */ -public class FilePickerActivity extends FolderPickerActivity { +class FilePickerActivity : FolderPickerActivity() { - @Override - public void onClick(View v) { - super.onClick(v); - } - - @Override - protected void createFragments() { - OCFileListFragment listOfFiles = new OCFileListFragment(); - Bundle args = new Bundle(); - args.putBoolean(OCFileListFragment.ARG_ONLY_FOLDERS_CLICKABLE, true); - args.putBoolean(OCFileListFragment.ARG_HIDE_FAB, true); - args.putBoolean(OCFileListFragment.ARG_HIDE_ITEM_OPTIONS, true); - args.putBoolean(OCFileListFragment.ARG_SEARCH_ONLY_FOLDER, false); - args.putBoolean(OCFileListFragment.ARG_FILE_SELECTABLE, true); - args.putString(OCFileListFragment.ARG_MIMETYPE, getIntent().getStringExtra(OCFileListFragment.ARG_MIMETYPE)); - listOfFiles.setArguments(args); - FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); - transaction.add(R.id.fragment_container, listOfFiles, TAG_LIST_OF_FOLDERS); - transaction.commit(); + override fun createFragments() { + val listOfFiles = OCFileListFragment() + val args = Bundle() + args.putBoolean(OCFileListFragment.ARG_ONLY_FOLDERS_CLICKABLE, true) + args.putBoolean(OCFileListFragment.ARG_HIDE_FAB, true) + args.putBoolean(OCFileListFragment.ARG_HIDE_ITEM_OPTIONS, true) + args.putBoolean(OCFileListFragment.ARG_SEARCH_ONLY_FOLDER, false) + args.putBoolean(OCFileListFragment.ARG_FILE_SELECTABLE, true) + args.putString(OCFileListFragment.ARG_MIMETYPE, intent.getStringExtra(OCFileListFragment.ARG_MIMETYPE)) + listOfFiles.arguments = args + val transaction = supportFragmentManager.beginTransaction() + transaction.add(R.id.fragment_container, listOfFiles, TAG_LIST_OF_FOLDERS) + transaction.commit() } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt index 97f7a24678..fced4e7cda 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.kt @@ -616,6 +616,7 @@ open class FolderPickerActivity : const val MOVE_OR_COPY = "MOVE_OR_COPY" const val CHOOSE_LOCATION = "CHOOSE_LOCATION" private val TAG = FolderPickerActivity::class.java.simpleName - protected const val TAG_LIST_OF_FOLDERS = "LIST_OF_FOLDERS" + + const val TAG_LIST_OF_FOLDERS = "LIST_OF_FOLDERS" } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.java deleted file mode 100644 index 21bc674acc..0000000000 --- a/app/src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.java +++ /dev/null @@ -1,373 +0,0 @@ -/* - * Nextcloud Android client application - * - * @author Andy Scherzinger - * @author Mario Danic - * @author Chris Narkiewicz - * Copyright (C) 2017 Andy Scherzinger - * Copyright (C) 2017 Mario Danic - * Copyright (C) 2020 Chris Narkiewicz - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.owncloud.android.ui.activity; - -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; - -import com.google.android.material.snackbar.Snackbar; -import com.nextcloud.client.account.User; -import com.nextcloud.client.account.UserAccountManager; -import com.nextcloud.client.jobs.NotificationWork; -import com.nextcloud.client.network.ClientFactory; -import com.nextcloud.java.util.Optional; -import com.owncloud.android.R; -import com.owncloud.android.databinding.NotificationsLayoutBinding; -import com.owncloud.android.datamodel.ArbitraryDataProvider; -import com.owncloud.android.datamodel.ArbitraryDataProviderImpl; -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.lib.resources.notifications.GetNotificationsRemoteOperation; -import com.owncloud.android.lib.resources.notifications.models.Notification; -import com.owncloud.android.ui.adapter.NotificationListAdapter; -import com.owncloud.android.ui.asynctasks.DeleteAllNotificationsTask; -import com.owncloud.android.ui.notifications.NotificationsContract; -import com.owncloud.android.utils.DisplayUtils; -import com.owncloud.android.utils.PushUtils; - -import java.util.List; - -import javax.inject.Inject; - -import androidx.annotation.VisibleForTesting; -import androidx.recyclerview.widget.LinearLayoutManager; - -/** - * Activity displaying all server side stored notification items. - */ -public class NotificationsActivity extends DrawerActivity implements NotificationsContract.View { - - private static final String TAG = NotificationsActivity.class.getSimpleName(); - - private NotificationsLayoutBinding binding; - private NotificationListAdapter adapter; - private Snackbar snackbar; - private OwnCloudClient client; - private Optional optionalUser; - - @Inject ClientFactory clientFactory; - - @Override - protected void onCreate(Bundle savedInstanceState) { - Log_OC.v(TAG, "onCreate() start"); - super.onCreate(savedInstanceState); - - binding = NotificationsLayoutBinding.inflate(getLayoutInflater()); - setContentView(binding.getRoot()); - - optionalUser = getUser(); - - // use account from intent (opened via android notification can have a different account than current one) - if (getIntent() != null && getIntent().getExtras() != null) { - String accountName = getIntent().getExtras().getString(NotificationWork.KEY_NOTIFICATION_ACCOUNT); - if (accountName != null && optionalUser.isPresent()) { - User user = optionalUser.get(); - if (user.getAccountName().equalsIgnoreCase(accountName)) { - accountManager.setCurrentOwnCloudAccount(accountName); - setUser(getUserAccountManager().getUser()); - optionalUser = getUser(); - } - } - } - - // setup toolbar - setupToolbar(); - - updateActionBarTitleAndHomeButtonByString(getString(R.string.drawer_item_notifications)); - - viewThemeUtils.androidx.themeSwipeRefreshLayout(binding.swipeContainingList); - viewThemeUtils.androidx.themeSwipeRefreshLayout(binding.swipeContainingEmpty); - - // setup drawer - setupDrawer(R.id.nav_notifications); - - if (!optionalUser.isPresent()) { - // show error - runOnUiThread(() -> setEmptyContent( - getString(R.string.notifications_no_results_headline), - getString(R.string.account_not_found)) - ); - return; - } - - binding.swipeContainingList.setOnRefreshListener(() -> { - setLoadingMessage(); - binding.swipeContainingList.setRefreshing(true); - fetchAndSetData(); - }); - - binding.swipeContainingEmpty.setOnRefreshListener(() -> { - setLoadingMessageEmpty(); - fetchAndSetData(); - }); - - setupPushWarning(); - setupContent(); - } - - private void setupPushWarning() { - if (!getResources().getBoolean(R.bool.show_push_warning)) { - return; - } - if (snackbar != null) { - if (!snackbar.isShown()) { - snackbar.show(); - } - } else { - String pushUrl = getResources().getString(R.string.push_server_url); - - if (pushUrl.isEmpty()) { - snackbar = Snackbar.make(binding.emptyList.emptyListView, - R.string.push_notifications_not_implemented, - Snackbar.LENGTH_INDEFINITE); - } else { - final ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProviderImpl(this); - final String accountName = optionalUser.isPresent() ? optionalUser.get().getAccountName() : ""; - final boolean usesOldLogin = arbitraryDataProvider.getBooleanValue(accountName, - UserAccountManager.ACCOUNT_USES_STANDARD_PASSWORD); - - if (usesOldLogin) { - snackbar = Snackbar.make(binding.emptyList.emptyListView, - R.string.push_notifications_old_login, - Snackbar.LENGTH_INDEFINITE); - } else { - String pushValue = arbitraryDataProvider.getValue(accountName, PushUtils.KEY_PUSH); - - if (pushValue == null || pushValue.isEmpty()) { - snackbar = Snackbar.make(binding.emptyList.emptyListView, - R.string.push_notifications_temp_error, - Snackbar.LENGTH_INDEFINITE); - } - } - } - - if (snackbar != null && !snackbar.isShown()) { - snackbar.show(); - } - } - } - - @Override - public void openDrawer() { - super.openDrawer(); - - if (snackbar != null && snackbar.isShown()) { - snackbar.dismiss(); - } - } - - @Override - public void closeDrawer() { - super.closeDrawer(); - - setupPushWarning(); - } - - /** - * sets up the UI elements and loads all notification items. - */ - private void setupContent() { - binding.emptyList.emptyListIcon.setImageResource(R.drawable.ic_notification); - setLoadingMessageEmpty(); - - LinearLayoutManager layoutManager = new LinearLayoutManager(this); - - binding.list.setLayoutManager(layoutManager); - - fetchAndSetData(); - } - - @VisibleForTesting - public void populateList(List notifications) { - initializeAdapter(); - adapter.setNotificationItems(notifications); - binding.loadingContent.setVisibility(View.GONE); - - if (notifications.size() > 0) { - binding.swipeContainingEmpty.setVisibility(View.GONE); - binding.swipeContainingList.setVisibility(View.VISIBLE); - } else { - setEmptyContent( - getString(R.string.notifications_no_results_headline), - getString(R.string.notifications_no_results_message) - ); - binding.swipeContainingList.setVisibility(View.GONE); - binding.swipeContainingEmpty.setVisibility(View.VISIBLE); - } - } - - private void fetchAndSetData() { - Thread t = new Thread(() -> { - initializeAdapter(); - - GetNotificationsRemoteOperation getRemoteNotificationOperation = new GetNotificationsRemoteOperation(); - final RemoteOperationResult> result = getRemoteNotificationOperation.execute(client); - - if (result.isSuccess() && result.getResultData() != null) { - runOnUiThread(() -> populateList(result.getResultData())); - } else { - Log_OC.d(TAG, result.getLogMessage()); - // show error - runOnUiThread(() -> setEmptyContent(getString(R.string.notifications_no_results_headline), result.getLogMessage())); - } - - hideRefreshLayoutLoader(); - }); - - t.start(); - } - - private void initializeClient() { - if (client == null && optionalUser.isPresent()) { - try { - User user = optionalUser.get(); - client = clientFactory.create(user); - } catch (ClientFactory.CreationException e) { - Log_OC.e(TAG, "Error initializing client", e); - } - } - } - - private void initializeAdapter() { - initializeClient(); - if (adapter == null) { - adapter = new NotificationListAdapter(client, this, viewThemeUtils); - binding.list.setAdapter(adapter); - } - } - - private void hideRefreshLayoutLoader() { - runOnUiThread(() -> { - binding.swipeContainingList.setRefreshing(false); - binding.swipeContainingEmpty.setRefreshing(false); - }); - } - - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.activity_notifications, menu); - - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - boolean retval = true; - - int itemId = item.getItemId(); - if (itemId == android.R.id.home) { - if (isDrawerOpen()) { - closeDrawer(); - } else { - openDrawer(); - } - } else if (itemId == R.id.action_empty_notifications) { - new DeleteAllNotificationsTask(client, this).execute(); - } else { - retval = super.onOptionsItemSelected(item); - } - - return retval; - } - - private void setLoadingMessage() { - binding.swipeContainingEmpty.setVisibility(View.GONE); - } - - @VisibleForTesting - public void setLoadingMessageEmpty() { - binding.swipeContainingList.setVisibility(View.GONE); - binding.emptyList.emptyListView.setVisibility(View.GONE); - binding.loadingContent.setVisibility(View.VISIBLE); - } - - @VisibleForTesting - public void setEmptyContent(String headline, String message) { - binding.swipeContainingList.setVisibility(View.GONE); - binding.loadingContent.setVisibility(View.GONE); - binding.swipeContainingEmpty.setVisibility(View.VISIBLE); - binding.emptyList.emptyListView.setVisibility(View.VISIBLE); - - binding.emptyList.emptyListViewHeadline.setText(headline); - binding.emptyList.emptyListViewText.setText(message); - binding.emptyList.emptyListIcon.setImageResource(R.drawable.ic_notification); - - binding.emptyList.emptyListViewText.setVisibility(View.VISIBLE); - binding.emptyList.emptyListIcon.setVisibility(View.VISIBLE); - } - - @Override - protected void onResume() { - super.onResume(); - setDrawerMenuItemChecked(R.id.nav_notifications); - } - - @Override - public void onRemovedNotification(boolean isSuccess) { - if (!isSuccess) { - DisplayUtils.showSnackMessage(this, getString(R.string.remove_notification_failed)); - fetchAndSetData(); - } - } - - @Override - public void removeNotification(NotificationListAdapter.NotificationViewHolder holder) { - adapter.removeNotification(holder); - - if (adapter.getItemCount() == 0) { - setEmptyContent(getString(R.string.notifications_no_results_headline), getString(R.string.notifications_no_results_message)); - binding.swipeContainingList.setVisibility(View.GONE); - binding.loadingContent.setVisibility(View.GONE); - binding.swipeContainingEmpty.setVisibility(View.VISIBLE); - } - } - - @Override - public void onRemovedAllNotifications(boolean isSuccess) { - if (isSuccess) { - adapter.removeAllNotifications(); - setEmptyContent(getString(R.string.notifications_no_results_headline), getString(R.string.notifications_no_results_message)); - binding.loadingContent.setVisibility(View.GONE); - binding.swipeContainingList.setVisibility(View.GONE); - binding.swipeContainingEmpty.setVisibility(View.VISIBLE); - } else { - DisplayUtils.showSnackMessage(this, getString(R.string.clear_notifications_failed)); - } - } - - @Override - public void onActionCallback(boolean isSuccess, - Notification notification, - NotificationListAdapter.NotificationViewHolder holder) { - if (isSuccess) { - adapter.removeNotification(holder); - } else { - adapter.setButtons(holder, notification); - DisplayUtils.showSnackMessage(this, getString(R.string.notification_action_failed)); - } - } -} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.kt new file mode 100644 index 0000000000..f0e5ec2cd8 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.kt @@ -0,0 +1,376 @@ +/* + * Nextcloud Android client application + * + * @author Andy Scherzinger + * @author Mario Danic + * @author Chris Narkiewicz + * Copyright (C) 2017 Andy Scherzinger + * Copyright (C) 2017 Mario Danic + * Copyright (C) 2020 Chris Narkiewicz + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package com.owncloud.android.ui.activity + +import android.os.Bundle +import android.view.Menu +import android.view.MenuItem +import android.view.View +import androidx.annotation.VisibleForTesting +import androidx.recyclerview.widget.LinearLayoutManager +import com.google.android.material.snackbar.Snackbar +import com.nextcloud.client.account.User +import com.nextcloud.client.account.UserAccountManager +import com.nextcloud.client.jobs.NotificationWork +import com.nextcloud.client.network.ClientFactory.CreationException +import com.nextcloud.java.util.Optional +import com.owncloud.android.R +import com.owncloud.android.databinding.NotificationsLayoutBinding +import com.owncloud.android.datamodel.ArbitraryDataProvider +import com.owncloud.android.datamodel.ArbitraryDataProviderImpl +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.lib.resources.notifications.GetNotificationsRemoteOperation +import com.owncloud.android.lib.resources.notifications.models.Notification +import com.owncloud.android.ui.adapter.NotificationListAdapter +import com.owncloud.android.ui.adapter.NotificationListAdapter.NotificationViewHolder +import com.owncloud.android.ui.asynctasks.DeleteAllNotificationsTask +import com.owncloud.android.ui.notifications.NotificationsContract +import com.owncloud.android.utils.DisplayUtils +import com.owncloud.android.utils.PushUtils + +/** + * Activity displaying all server side stored notification items. + */ +class NotificationsActivity : DrawerActivity(), NotificationsContract.View { + + private lateinit var binding: NotificationsLayoutBinding + + private var adapter: NotificationListAdapter? = null + private var snackbar: Snackbar? = null + private var client: OwnCloudClient? = null + private var optionalUser: Optional? = null + + override fun onCreate(savedInstanceState: Bundle?) { + Log_OC.v(TAG, "onCreate() start") + + super.onCreate(savedInstanceState) + + binding = NotificationsLayoutBinding.inflate(layoutInflater) + setContentView(binding.root) + + optionalUser = user + + intent?.let { + it.extras?.let { bundle -> + setupUser(bundle) + } + } + + setupToolbar() + updateActionBarTitleAndHomeButtonByString(getString(R.string.drawer_item_notifications)) + setupDrawer(R.id.nav_notifications) + + if (optionalUser?.isPresent == false) { + showError() + } + + setupContainingList() + setupPushWarning() + setupContent() + } + + private fun setupContainingList() { + viewThemeUtils.androidx.themeSwipeRefreshLayout(binding.swipeContainingList) + viewThemeUtils.androidx.themeSwipeRefreshLayout(binding.swipeContainingEmpty) + binding.swipeContainingList.setOnRefreshListener { + setLoadingMessage() + binding.swipeContainingList.isRefreshing = true + fetchAndSetData() + } + binding.swipeContainingEmpty.setOnRefreshListener { + setLoadingMessageEmpty() + fetchAndSetData() + } + } + + private fun setupUser(bundle: Bundle) { + val accountName = bundle.getString(NotificationWork.KEY_NOTIFICATION_ACCOUNT) + + if (accountName != null && optionalUser?.isPresent == true) { + val user = optionalUser?.get() + if (user?.accountName.equals(accountName, ignoreCase = true)) { + accountManager.setCurrentOwnCloudAccount(accountName) + setUser(userAccountManager.user) + optionalUser = getUser() + } + } + } + + private fun showError() { + runOnUiThread { + setEmptyContent( + getString(R.string.notifications_no_results_headline), + getString(R.string.account_not_found) + ) + } + return + } + + private fun setupPushWarning() { + if (!resources.getBoolean(R.bool.show_push_warning)) { + return + } + + if (snackbar != null) { + if (snackbar?.isShown == false) { + snackbar?.show() + } + } else { + val pushUrl = resources.getString(R.string.push_server_url) + if (pushUrl.isEmpty()) { + snackbar = Snackbar.make( + binding.emptyList.emptyListView, + R.string.push_notifications_not_implemented, + Snackbar.LENGTH_INDEFINITE + ) + } else { + val arbitraryDataProvider: ArbitraryDataProvider = ArbitraryDataProviderImpl(this) + val accountName: String = if (optionalUser?.isPresent == true) { + optionalUser?.get()?.accountName ?: "" + } else { + "" + } + val usesOldLogin = arbitraryDataProvider.getBooleanValue( + accountName, + UserAccountManager.ACCOUNT_USES_STANDARD_PASSWORD + ) + + if (usesOldLogin) { + snackbar = Snackbar.make( + binding.emptyList.emptyListView, + R.string.push_notifications_old_login, + Snackbar.LENGTH_INDEFINITE + ) + } else { + val pushValue = arbitraryDataProvider.getValue(accountName, PushUtils.KEY_PUSH) + if (pushValue.isEmpty()) { + snackbar = Snackbar.make( + binding.emptyList.emptyListView, + R.string.push_notifications_temp_error, + Snackbar.LENGTH_INDEFINITE + ) + } + } + } + + if (snackbar != null && snackbar?.isShown == false) { + snackbar?.show() + } + } + } + + override fun openDrawer() { + super.openDrawer() + if (snackbar != null && snackbar?.isShown == true) { + snackbar?.dismiss() + } + } + + override fun closeDrawer() { + super.closeDrawer() + setupPushWarning() + } + + /** + * sets up the UI elements and loads all notification items. + */ + private fun setupContent() { + binding.emptyList.emptyListIcon.setImageResource(R.drawable.ic_notification) + setLoadingMessageEmpty() + val layoutManager = LinearLayoutManager(this) + binding.list.layoutManager = layoutManager + fetchAndSetData() + } + + @VisibleForTesting + fun populateList(notifications: List?) { + initializeAdapter() + adapter?.setNotificationItems(notifications) + binding.loadingContent.visibility = View.GONE + + if (notifications?.isNotEmpty() == true) { + binding.swipeContainingEmpty.visibility = View.GONE + binding.swipeContainingList.visibility = View.VISIBLE + } else { + setEmptyContent( + getString(R.string.notifications_no_results_headline), + getString(R.string.notifications_no_results_message) + ) + binding.swipeContainingList.visibility = View.GONE + binding.swipeContainingEmpty.visibility = View.VISIBLE + } + } + + private fun fetchAndSetData() { + val t = Thread { + initializeAdapter() + val getRemoteNotificationOperation = GetNotificationsRemoteOperation() + val result = getRemoteNotificationOperation.execute(client) + if (result.isSuccess && result.resultData != null) { + runOnUiThread { populateList(result.resultData) } + } else { + Log_OC.d(TAG, result.logMessage) + // show error + runOnUiThread { + setEmptyContent( + getString(R.string.notifications_no_results_headline), + result.logMessage + ) + } + } + hideRefreshLayoutLoader() + } + t.start() + } + + private fun initializeClient() { + if (client == null && optionalUser?.isPresent == true) { + try { + val user = optionalUser?.get() + client = clientFactory.create(user) + } catch (e: CreationException) { + Log_OC.e(TAG, "Error initializing client", e) + } + } + } + + private fun initializeAdapter() { + initializeClient() + if (adapter == null) { + adapter = NotificationListAdapter(client, this, viewThemeUtils) + binding.list.adapter = adapter + } + } + + private fun hideRefreshLayoutLoader() { + runOnUiThread { + binding.swipeContainingList.isRefreshing = false + binding.swipeContainingEmpty.isRefreshing = false + } + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.activity_notifications, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + var retval = true + val itemId = item.itemId + if (itemId == android.R.id.home) { + if (isDrawerOpen) { + closeDrawer() + } else { + openDrawer() + } + } else if (itemId == R.id.action_empty_notifications) { + DeleteAllNotificationsTask(client, this).execute() + } else { + retval = super.onOptionsItemSelected(item) + } + return retval + } + + private fun setLoadingMessage() { + binding.swipeContainingEmpty.visibility = View.GONE + } + + @VisibleForTesting + fun setLoadingMessageEmpty() { + binding.swipeContainingList.visibility = View.GONE + binding.emptyList.emptyListView.visibility = View.GONE + binding.loadingContent.visibility = View.VISIBLE + } + + @VisibleForTesting + fun setEmptyContent(headline: String?, message: String?) { + binding.swipeContainingList.visibility = View.GONE + binding.loadingContent.visibility = View.GONE + binding.swipeContainingEmpty.visibility = View.VISIBLE + binding.emptyList.emptyListView.visibility = View.VISIBLE + binding.emptyList.emptyListViewHeadline.text = headline + binding.emptyList.emptyListViewText.text = message + binding.emptyList.emptyListIcon.setImageResource(R.drawable.ic_notification) + binding.emptyList.emptyListViewText.visibility = View.VISIBLE + binding.emptyList.emptyListIcon.visibility = View.VISIBLE + } + + override fun onResume() { + super.onResume() + setDrawerMenuItemChecked(R.id.nav_notifications) + } + + override fun onRemovedNotification(isSuccess: Boolean) { + if (!isSuccess) { + DisplayUtils.showSnackMessage(this, getString(R.string.remove_notification_failed)) + fetchAndSetData() + } + } + + override fun removeNotification(holder: NotificationViewHolder) { + adapter?.removeNotification(holder) + if (adapter?.itemCount == 0) { + setEmptyContent( + getString(R.string.notifications_no_results_headline), + getString(R.string.notifications_no_results_message) + ) + binding.swipeContainingList.visibility = View.GONE + binding.loadingContent.visibility = View.GONE + binding.swipeContainingEmpty.visibility = View.VISIBLE + } + } + + override fun onRemovedAllNotifications(isSuccess: Boolean) { + if (isSuccess) { + adapter?.removeAllNotifications() + setEmptyContent( + getString(R.string.notifications_no_results_headline), + getString(R.string.notifications_no_results_message) + ) + binding.loadingContent.visibility = View.GONE + binding.swipeContainingList.visibility = View.GONE + binding.swipeContainingEmpty.visibility = View.VISIBLE + } else { + DisplayUtils.showSnackMessage(this, getString(R.string.clear_notifications_failed)) + } + } + + override fun onActionCallback( + isSuccess: Boolean, + notification: Notification, + holder: NotificationViewHolder + ) { + if (isSuccess) { + adapter?.removeNotification(holder) + } else { + adapter?.setButtons(holder, notification) + DisplayUtils.showSnackMessage(this, getString(R.string.notification_action_failed)) + } + } + + companion object { + private val TAG = NotificationsActivity::class.java.simpleName + } +} diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index d74e021324..3874b9b1c1 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -325,7 +325,6 @@ حذف خطأ في استرداد النشاطات للملف حدث خطأ في تحميل التفاصيل - تنزيل \u0020 الملف حفظ قم برفع بعض المحتوى أو زامن مع أجهزتك diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml index 0aa6761dc3..179ba7581d 100644 --- a/app/src/main/res/values-cs-rCZ/strings.xml +++ b/app/src/main/res/values-cs-rCZ/strings.xml @@ -325,7 +325,6 @@ Smazat Při načítání aktivit u souboru došlo k chybě Nepodařilo se načíst podrobnosti. - Stahování \u0020 Soubor Ponechat Nahrajte nějaký obsah, nebo synchronizujte s vašimi zařízeními. diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 5c024cac6f..cf4e85d7e5 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -325,7 +325,6 @@ Slet Fejl ved indlæsning af aktiviteter for fil Fejl ved indlæsning af detaljer - Downloader \u0020 Fil Behold Upload indhold eller synkronisér med dine enheder. diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index e6c62482f2..fe479e5e48 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -325,7 +325,6 @@ Löschen Fehler beim Abrufen der Aktivitäten für die Datei Fehler beim Laden der Details - Herunterladen \u0020 Datei Behalten Laden Sie Inhalt hoch oder synchronisieren Sie mit Ihren Geräten. diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 0c0202b9fc..03e2d5ca0e 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -327,7 +327,6 @@ Attention, la suppression est irréversible. Supprimer Erreur lors de la récupération de l’activité du fichier Impossible de charger les détails - Téléchargement de \u0020 Fichier Conserver Déposez du contenu ou synchronisez vos appareils. diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 5117e01527..0c6f74392c 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -325,7 +325,6 @@ Eliminar Produciuse un erro ao recuperar actividades para o ficheiro Produciuse un fallo ao cargar os detalles - Descargando \u0020 Ficheiro Conservar Envíe algún contido ou sincronice cos seus dispositivos. diff --git a/app/src/main/res/values-hu-rHU/strings.xml b/app/src/main/res/values-hu-rHU/strings.xml index a68e13ba8a..41bd2a80b1 100644 --- a/app/src/main/res/values-hu-rHU/strings.xml +++ b/app/src/main/res/values-hu-rHU/strings.xml @@ -325,7 +325,6 @@ Törlés Hiba a fájl tevékenységeinek lekérésekor A részletek betöltése sikertelen - Letöltés \u0020 Fájl Megtartás Töltsön fel új tartalmat vagy szinkronizáljon az eszközeivel diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 60b032fda9..bce95d1a08 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -325,7 +325,6 @@ Usuń Błąd podczas pobierania aktywności dla pliku Nie udało się załadować szczegółów - Pobieranie \u0020 Plik Zachowaj Wyślij lub zsynchronizuj pliki z urządzeniami. diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index c406d92a02..09b3d35379 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -325,7 +325,6 @@ Удалить Ошибка получения истории событий, связанных с файлом Не удалось получить подробные сведения - Скачивание \u0020 Файл Сохранить Добавьте что-нибудь или синхронизируйте со своими устройствами! diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 36ad0cb6c7..295eeef96f 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -325,7 +325,6 @@ Обриши Грешка при добављању активности за фајл Грешка при учитавању детаља - Преузима се \u0020 Фајл Задржи Отпремите неки садржај или синхронизујте са вашим уређајима. diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 23cb596699..84ab136db7 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -325,7 +325,6 @@ Ta bort Fel vid hämtning av aktiviteter för fil Kunde inte läsa in detaljer - Laddar ner \u0020 Fil Behåll Ladda upp något eller synkronisera med dina enheter diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 3384b39a1c..cc435ed7d9 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -325,7 +325,6 @@ Sil Dosya işlemleri alınırken sorun çıktı Ayrıntılar yüklenemedi - İndiriliyor \u0020 Dosya Tut Bazı içerikler yükleyin ya da aygıtlarınızla eşitleyin. diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 8ed39ee928..c66b1fde90 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -323,7 +323,6 @@ Вилучити Помилка з отриманням дії для файлу Не вдалося завантажити подробиці - Звантаження \u0020 Файл Зберегти Додати дані або синхронізувати з вашими пристроями. diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 8a2916edba..4651365872 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -325,7 +325,6 @@ 删除 获取文件动态时出错 加载详情失败 - 下载中 \u0020 文件 保留 上传一些内容或与您的设备同步。 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 52f42a3065..8f94353c5c 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -325,7 +325,6 @@ 刪除 取得檔案活動時發生錯誤 載入詳細資訊失敗 - 正在下載 \u0020 檔案 保留 上傳一些內容或與您的裝置同步。 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d552f8c69b..4c293c2b93 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -162,7 +162,6 @@ Fetching server version… Waiting to upload %1$s (%2$d) - Downloading \u0020 Downloading… %1$d%% Downloading %2$s Downloaded diff --git a/gradle.properties b/gradle.properties index 85bc2ba7da..5b47fafb59 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,5 +9,10 @@ android.nonTransitiveRClass=false android.nonFinalResIds=false #android.debug.obsoleteApi=true -# Minimum max heap space to get reliable builds -org.gradle.jvmargs=-Xmx1g + +# JVM arguments to optimize heap usage, enable heap dump on out-of-memory errors, and set the file encoding +org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +kotlin.daemon.jvmargs=-Xmx4096m +org.gradle.caching=true +org.gradle.parallel=true +org.gradle.configureondemand=true diff --git a/scripts/analysis/lint-results.txt b/scripts/analysis/lint-results.txt index b8212f767a..17a46e7806 100644 --- a/scripts/analysis/lint-results.txt +++ b/scripts/analysis/lint-results.txt @@ -1,2 +1,2 @@ DO NOT TOUCH; GENERATED BY DRONE - Lint Report: 75 warnings + Lint Report: 74 warnings