Skip to content
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
6 changes: 6 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@
android:label="@string/app_name"
android:theme="@style/Theme.App.Starting"
android:exported="true">

<intent-filter>
<action android:name="com.nextcloud.intent.OPEN_ECOSYSTEM_APP" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>

<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,13 @@
import android.content.Intent;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import androidx.activity.OnBackPressedCallback;
import androidx.annotation.ColorInt;
Expand All @@ -46,7 +41,6 @@
import androidx.appcompat.app.ActionBarDrawerToggle;
import androidx.appcompat.view.ActionMode;
import androidx.appcompat.widget.SearchView;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
Expand All @@ -67,6 +61,9 @@
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import com.nextcloud.android.common.core.utils.ecosystem.AccountReceiverCallback;
import com.nextcloud.android.common.core.utils.ecosystem.EcosystemApp;
import com.nextcloud.android.common.core.utils.ecosystem.EcosystemManager;
import com.nextcloud.android.common.ui.theme.utils.ColorRole;
import com.nextcloud.android.sso.AccountImporter;
import com.nextcloud.android.sso.exceptions.AccountImportCancelledException;
Expand All @@ -76,16 +73,16 @@
import com.nextcloud.android.sso.exceptions.TokenMismatchException;
import com.nextcloud.android.sso.exceptions.UnknownErrorException;
import com.nextcloud.android.sso.helper.SingleAccountHelper;
import com.owncloud.android.lib.common.utils.Log_OC;

import org.jetbrains.annotations.NotNull;

import java.net.HttpURLConnection;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

import hct.Hct;
import it.niedermann.android.util.ColorUtil;
import it.niedermann.owncloud.notes.LockedActivity;
import it.niedermann.owncloud.notes.NotesApplication;
Expand Down Expand Up @@ -121,6 +118,7 @@
import it.niedermann.owncloud.notes.shared.model.NavigationCategory;
import it.niedermann.owncloud.notes.shared.model.NoteClickListener;
import it.niedermann.owncloud.notes.shared.util.CustomAppGlideModule;
import it.niedermann.owncloud.notes.shared.util.DisplayUtils;
import it.niedermann.owncloud.notes.shared.util.NoteUtil;
import it.niedermann.owncloud.notes.shared.util.ShareUtil;
import it.niedermann.owncloud.notes.util.LinkHelper;
Expand All @@ -133,6 +131,8 @@ public class MainActivity extends LockedActivity implements NoteClickListener, A

protected MainViewModel mainViewModel;

private EcosystemManager ecosystemManager;

private boolean gridView = true;

public static final String ADAPTER_KEY_RECENT = "recent";
Expand Down Expand Up @@ -173,6 +173,8 @@ protected void onCreate(Bundle savedInstanceState) {

setContentView(binding.getRoot());

ecosystemManager = new EcosystemManager(this);
handleEcosystemIntent(getIntent());
this.coordinatorLayout = binding.activityNotesListView.activityNotesListView;
this.swipeRefreshLayout = binding.activityNotesListView.swiperefreshlayout;
this.fabCreate = binding.activityNotesListView.fabCreate;
Expand Down Expand Up @@ -379,12 +381,20 @@ private void setupDrawerAppMenu() {
}

private void setupDrawerAppMenuListener() {
// Add listeners to the ecosystem items to launch the app or app-store
binding.drawerEcosystemFiles.setOnClickListener(v -> LinkHelper.INSTANCE.openAppOrStore(LinkHelper.APP_NEXTCLOUD_FILES, mainViewModel.getCurrentAccount().getValue().getAccountName(), this));
binding.drawerEcosystemTalk.setOnClickListener(v -> LinkHelper.INSTANCE.openAppOrStore(LinkHelper.APP_NEXTCLOUD_TALK, mainViewModel.getCurrentAccount().getValue().getAccountName(), this));
binding.drawerEcosystemFiles.setOnClickListener(v -> ecosystemManager.openApp(EcosystemApp.FILES, getAccountName()));
binding.drawerEcosystemTalk.setOnClickListener(v -> ecosystemManager.openApp(EcosystemApp.TALK, getAccountName()));
binding.drawerEcosystemMore.setOnClickListener(v -> LinkHelper.INSTANCE.openAppStore("Nextcloud", true, this));
}

private String getAccountName() {
final var currentAccount = mainViewModel.getCurrentAccount().getValue();
if (currentAccount == null) {
return null;
}

return currentAccount.getAccountName();
}

private void themeDrawerAppMenu(int color) {
ColorStateList colorStateList = ColorStateList.valueOf(color);
binding.drawerEcosystemFilesIcon.setImageTintList(colorStateList);
Expand Down Expand Up @@ -482,6 +492,26 @@ protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
mainViewModel.restoreInstanceState();
}

private void handleEcosystemIntent(Intent intent) {
ecosystemManager.receiveAccount(intent, new AccountReceiverCallback() {
@Override
public void onAccountReceived(@NotNull String accountName) {
final var account = mainViewModel.getAccountByName(accountName);
if (account != null) {
onAccountChosen(account);
} else {
Log_OC.w(TAG, "account not found");
DisplayUtils.showSnackMessage(MainActivity.this, R.string.account_not_found);
}
}

@Override
public void onAccountError(@NotNull String reason) {
Log_OC.w(TAG, "handleEcosystemIntent: " + reason);
}
});
}

private void setupToolbars() {
setSupportActionBar(binding.activityNotesListView.searchToolbar);
activityBinding.searchBar.homeToolbar.setOnClickListener((v) -> {
Expand Down Expand Up @@ -754,6 +784,7 @@ protected void onNewIntent(Intent intent) {
activityBinding.searchView.setQuery(intent.getStringExtra(SearchManager.QUERY), true);
}
super.onNewIntent(intent);
handleEcosystemIntent(intent);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ public LiveData<Account> getCurrentAccount() {
return distinctUntilChanged(currentAccount);
}

public Account getAccountByName(String accountName) {
return repo.getAccountByName(accountName);
}

public void postCurrentAccount(@NonNull Account account) {
state.set(KEY_CURRENT_ACCOUNT, account);
BrandingUtil.saveBrandColor(getApplication(), account.getColor());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,46 +10,16 @@ package it.niedermann.owncloud.notes.util
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.core.net.toUri
import com.owncloud.android.lib.common.utils.Log_OC
import java.util.Locale

/**
* Helper class for opening Nextcloud apps if present
* or falling back to opening the app store
* in case the app is not yet installed on the device.
*/
object LinkHelper {
const val APP_NEXTCLOUD_FILES = "com.nextcloud.client"
const val APP_NEXTCLOUD_NOTES = "it.niedermann.owncloud.notes"
const val APP_NEXTCLOUD_TALK = "com.nextcloud.talk2"
const val KEY_ACCOUNT: String = "KEY_ACCOUNT"
private const val TAG = "LinkHelper"

/**
* Open specified app and, if not installed redirect to corresponding download.
*
* @param packageName of app to be opened
* @param userHash to pass in intent
*/
fun openAppOrStore(
packageName: String,
userHash: String?,
context: Context,
) {
val intent = context.packageManager.getLaunchIntentForPackage(packageName)
if (intent != null) {
// app installed - open directly
// TODO handle null user?
intent.putExtra(KEY_ACCOUNT, userHash)
context.startActivity(intent)
} else {
// app not found - open market (Google Play Store, F-Droid, etc.)
openAppStore(packageName, false, context)
}
}

/**
* Open app store page of specified app or search for specified string. Will attempt to open browser when no app
* store is available.
Expand All @@ -66,7 +36,7 @@ object LinkHelper {
val intent = Intent(Intent.ACTION_VIEW, "market://$suffix".toUri())
try {
context.startActivity(intent)
} catch (activityNotFoundException1: ActivityNotFoundException) {
} catch (_: ActivityNotFoundException) {
// all is lost: open google play store web page for app
if (!search) {
suffix = "apps/$suffix"
Expand All @@ -75,58 +45,4 @@ object LinkHelper {
context.startActivity(intent)
}
}

// region Validation
private const val HTTP = "http"
private const val HTTPS = "https"
private const val FILE = "file"
private const val CONTENT = "content"

/**
* Validates if a string can be converted to a valid URI
*/
@Suppress("TooGenericExceptionCaught", "ReturnCount")
fun validateAndGetURI(uriString: String?): Uri? {
if (uriString.isNullOrBlank()) {
Log_OC.w(TAG, "Given uriString is null or blank")
return null
}

return try {
val uri = uriString.toUri()
if (uri.scheme == null) {
return null
}

val validSchemes = listOf(HTTP, HTTPS, FILE, CONTENT)
if (uri.scheme in validSchemes) uri else null
} catch (e: Exception) {
Log_OC.e(TAG, "Invalid URI string: $uriString -- $e")
null
}
}

/**
* Validates if a URL string is valid
*/
@Suppress("TooGenericExceptionCaught", "ReturnCount")
fun validateAndGetURL(url: String?): String? {
if (url.isNullOrBlank()) {
Log_OC.w(TAG, "Given url is null or blank")
return null
}

return try {
val uri = url.toUri()
if (uri.scheme == null) {
return null
}
val validSchemes = listOf(HTTP, HTTPS)
if (uri.scheme in validSchemes) url else null
} catch (e: Exception) {
Log_OC.e(TAG, "Invalid URL: $url -- $e")
null
}
}
// endregion
}
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -520,4 +520,5 @@
<string name="activity_sharing_details_title">Sharing Details</string>
<string name="share_expires">Share expires on %1$s</string>
<string name="dismiss">Dismiss</string>
<string name="account_not_found">Account not found!</string>
</resources>
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ buildscript {
kotlinVersion = '2.3.0'
commonsVersion = '2.4.1'
androidCommonsVersion = '1.1.1'
nextcloudAndroidCommonLib = "0.31.0"
nextcloudAndroidCommonLib = "3babd42636"
singleSignOnVersion = "1.3.4"
}
repositories {
Expand Down
Loading
Loading