diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer.png b/app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer.png index 54cab02a6518..e5bcdfb92b83 100644 Binary files a/app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer.png and b/app/screenshots/gplay/debug/com.nextcloud.client.ActivitiesActivityIT_openDrawer.png differ diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.FileDisplayActivityScreenshotIT_drawer.png b/app/screenshots/gplay/debug/com.nextcloud.client.FileDisplayActivityScreenshotIT_drawer.png index c903daa43655..d1da2e865a1a 100644 Binary files a/app/screenshots/gplay/debug/com.nextcloud.client.FileDisplayActivityScreenshotIT_drawer.png and b/app/screenshots/gplay/debug/com.nextcloud.client.FileDisplayActivityScreenshotIT_drawer.png differ diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.SettingsActivityIT_open.png b/app/screenshots/gplay/debug/com.nextcloud.client.SettingsActivityIT_open.png index 47591b4ffc85..210e2bc908b2 100644 Binary files a/app/screenshots/gplay/debug/com.nextcloud.client.SettingsActivityIT_open.png and b/app/screenshots/gplay/debug/com.nextcloud.client.SettingsActivityIT_open.png differ diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.SettingsActivityIT_showMnemonic_Error.png b/app/screenshots/gplay/debug/com.nextcloud.client.SettingsActivityIT_showMnemonic_Error.png index aa62a6bfce97..aa73967aff3c 100644 Binary files a/app/screenshots/gplay/debug/com.nextcloud.client.SettingsActivityIT_showMnemonic_Error.png and b/app/screenshots/gplay/debug/com.nextcloud.client.SettingsActivityIT_showMnemonic_Error.png differ diff --git a/app/screenshots/gplay/debug/com.nextcloud.client.UploadListActivityActivityIT_openDrawer.png b/app/screenshots/gplay/debug/com.nextcloud.client.UploadListActivityActivityIT_openDrawer.png index ac1ac9e81b11..c90f015ebce1 100644 Binary files a/app/screenshots/gplay/debug/com.nextcloud.client.UploadListActivityActivityIT_openDrawer.png and b/app/screenshots/gplay/debug/com.nextcloud.client.UploadListActivityActivityIT_openDrawer.png differ diff --git a/app/src/main/java/com/nextcloud/client/preferences/AppPreferences.java b/app/src/main/java/com/nextcloud/client/preferences/AppPreferences.java index eeaa93a1a53b..be26f2af8efa 100644 --- a/app/src/main/java/com/nextcloud/client/preferences/AppPreferences.java +++ b/app/src/main/java/com/nextcloud/client/preferences/AppPreferences.java @@ -75,6 +75,9 @@ default void onDarkThemeModeChanged(DarkMode mode) { boolean isShowHiddenFilesEnabled(); void setShowHiddenFilesEnabled(boolean enabled); + boolean isShowEcosystemApps(); + void setShowEcosystemApps(boolean enabled); + /** * Gets the selected file extension position the user selected to do the * last upload of a url file shared from other app. diff --git a/app/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java b/app/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java index d372a5ecf36d..97f60149d590 100644 --- a/app/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java +++ b/app/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java @@ -76,6 +76,7 @@ public final class AppPreferencesImpl implements AppPreferences { private static final String PREF__INSTANT_UPLOADING = "instant_uploading"; private static final String PREF__INSTANT_VIDEO_UPLOADING = "instant_video_uploading"; private static final String PREF__SHOW_HIDDEN_FILES = "show_hidden_files_pref"; + private static final String PREF__SHOW_ECOSYSTEM_APPS = "show_ecosystem_apps"; private static final String PREF__LEGACY_CLEAN = "legacyClean"; private static final String PREF__KEYS_MIGRATION = "keysMigration"; private static final String PREF__FIX_STORAGE_PATH = "storagePathFix"; @@ -221,6 +222,16 @@ public void setShowHiddenFilesEnabled(boolean enabled) { preferences.edit().putBoolean(PREF__SHOW_HIDDEN_FILES, enabled).apply(); } + @Override + public boolean isShowEcosystemApps() { + return preferences.getBoolean(PREF__SHOW_ECOSYSTEM_APPS, true); + } + + @Override + public void setShowEcosystemApps(boolean enabled) { + preferences.edit().putBoolean(PREF__SHOW_ECOSYSTEM_APPS, enabled).apply(); + } + @Override public int getUploadUrlFileExtensionUrlSelectedPos() { return preferences.getInt(AUTO_PREF__UPLOAD_FILE_EXTENSION_URL, 0); diff --git a/app/src/main/java/com/owncloud/android/authentication/DeepLinkLoginActivity.java b/app/src/main/java/com/owncloud/android/authentication/DeepLinkLoginActivity.java index ecfd878d4e4c..f99f65e5fd0b 100644 --- a/app/src/main/java/com/owncloud/android/authentication/DeepLinkLoginActivity.java +++ b/app/src/main/java/com/owncloud/android/authentication/DeepLinkLoginActivity.java @@ -30,7 +30,7 @@ protected void onCreate(Bundle savedInstanceState) { LoginUrlInfo loginUrlInfo = parseLoginDataUrl(prefix, data.toString()); TextView loginText = findViewById(R.id.loginInfo); - loginText.setText(String.format("Login with %1$s to %2$s", loginUrlInfo.username, + loginText.setText(String.format(getString(R.string.direct_login_text), loginUrlInfo.username, loginUrlInfo.serverAddress)); } catch (IllegalArgumentException e) { Toast.makeText(this, R.string.direct_login_failed, Toast.LENGTH_LONG).show(); diff --git a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index d47ad343c812..9e37e03e0e00 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -34,11 +34,14 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; +import android.content.res.ColorStateList; import android.content.res.Configuration; import android.graphics.Bitmap; +import android.graphics.Color; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.graphics.drawable.GradientDrawable; import android.graphics.drawable.LayerDrawable; import android.net.Uri; import android.os.Bundle; @@ -48,8 +51,6 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.view.ViewGroup.LayoutParams; -import android.view.ViewGroup.MarginLayoutParams; import android.webkit.URLUtil; import android.widget.ImageView; import android.widget.LinearLayout; @@ -133,6 +134,7 @@ import androidx.core.view.GravityCompat; import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.Fragment; +import hct.Hct; /** * Base class to handle setup of the drawer implementation including user switching and avatar fetching and fallback @@ -320,7 +322,7 @@ public void updateHeader() { .decoder(new SvgOrImageDecoder()); // background image - SimpleTarget target = new SimpleTarget() { + SimpleTarget target = new SimpleTarget<>() { @Override public void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) { @@ -332,7 +334,8 @@ public void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) { logo = BitmapUtils.scaleBitmap(resource, MAX_LOGO_SIZE_PX, width, height, max); } - Drawable[] drawables = {new ColorDrawable(primaryColor), new BitmapDrawable(logo)}; + Drawable[] drawables = {new ColorDrawable(primaryColor), + new BitmapDrawable(getResources(), logo)}; LayerDrawable layerDrawable = new LayerDrawable(drawables); String name = capability.getServerName(); @@ -345,29 +348,91 @@ public void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) { .load(Uri.parse(logo)) .into(target); } + + // hide ecosystem apps according to user preference or in branded client + LinearLayout ecosystemApps = mNavigationViewHeader.findViewById(R.id.drawer_ecosystem_apps); + if (getResources().getBoolean(R.bool.is_branded_client) || !preferences.isShowEcosystemApps()) { + ecosystemApps.setVisibility(View.GONE); + } else { + LinearLayout[] views = { + ecosystemApps.findViewById(R.id.drawer_ecosystem_notes), + ecosystemApps.findViewById(R.id.drawer_ecosystem_talk), + ecosystemApps.findViewById(R.id.drawer_ecosystem_more) + }; + + views[0].setOnClickListener(v -> openAppOrStore("it.niedermann.owncloud.notes")); + views[1].setOnClickListener(v -> openAppOrStore("com.nextcloud.talk2")); + views[2].setOnClickListener(v -> openAppStore("Nextcloud", true)); + + int iconColor; + if (Hct.fromInt(primaryColor).getTone() < 80.0) { + iconColor = Color.WHITE; + } else { + iconColor = getColor(R.color.grey_800_transparent); + } + for (LinearLayout view : views) { + ImageView imageView = (ImageView) view.getChildAt(0); + imageView.setImageTintList(ColorStateList.valueOf(iconColor)); + GradientDrawable background = (GradientDrawable) imageView.getBackground(); + background.setStroke(DisplayUtils.convertDpToPixel(1, this), iconColor); + TextView textView = (TextView) view.getChildAt(1); + textView.setTextColor(iconColor); + } + + ecosystemApps.setVisibility(View.VISIBLE); + } } } - private void setDrawerHeaderLogo(Drawable drawable, String name) { + /** + * Open specified app and, if not installed redirect to corresponding download. + * + * @param packageName of app to be opened + */ + private void openAppOrStore(String packageName) { + Intent intent = getPackageManager().getLaunchIntentForPackage(packageName); + if (intent != null) { + // app installed - open directly + intent.putExtra(FileDisplayActivity.KEY_ACCOUNT, getUser().get().hashCode()); + startActivity(intent); + } else { + // app not found - open market (Google Play Store, F-Droid, etc.) + openAppStore(packageName, false); + } + } + + /** + * Open app store page of specified app or search for specified string. + * Will attempt to open browser when no app store is available. + * + * @param string packageName or url-encoded search string + * @param search false -> show app corresponding to packageName; true -> open search for string + */ + private void openAppStore(String string, boolean search) { + String suffix = (search ? "search?q=" : "details?id=") + string; + Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://" + suffix)); + try { + startActivity(intent); + } catch (android.content.ActivityNotFoundException activityNotFoundException1) { + // all is lost: open google play store web page for app + if (!search) { + suffix = "apps/" + suffix; + } + intent.setData(Uri.parse("https://play.google.com/store/" + suffix)); + startActivity(intent); + } + } + + private void setDrawerHeaderLogo(Drawable drawable, String serverName) { ImageView imageHeader = mNavigationViewHeader.findViewById(R.id.drawer_header_logo); imageHeader.setImageDrawable(drawable); - imageHeader.setScaleType(ImageView.ScaleType.FIT_START); imageHeader.setAdjustViewBounds(true); - imageHeader.setMaxWidth(DisplayUtils.convertDpToPixel(100f, this)); - - MarginLayoutParams oldParam = (MarginLayoutParams) imageHeader.getLayoutParams(); - MarginLayoutParams params = new MarginLayoutParams(LayoutParams.WRAP_CONTENT, - LayoutParams.MATCH_PARENT); - params.leftMargin = oldParam.leftMargin; - params.rightMargin = oldParam.rightMargin; - - imageHeader.setLayoutParams(new LinearLayout.LayoutParams(params)); - - if (!TextUtils.isEmpty(name)) { - TextView serverName = mNavigationViewHeader.findViewById(R.id.drawer_header_server_name); - serverName.setText(name); - serverName.setTextColor(themeColorUtils.unchangedFontColor(this)); + if (!TextUtils.isEmpty(serverName)) { + TextView serverNameView = mNavigationViewHeader.findViewById(R.id.drawer_header_server_name); + serverNameView.setVisibility(View.VISIBLE); + serverNameView.setText(serverName); + serverNameView.setTextColor(themeColorUtils.unchangedFontColor(this)); } } @@ -653,11 +718,7 @@ protected void updateActionBarTitleAndHomeButton(OCFile chosenFile) { // set home button properties if (mDrawerToggle != null) { - if (chosenFile != null && isRoot(chosenFile)) { - mDrawerToggle.setDrawerIndicatorEnabled(true); - } else { - mDrawerToggle.setDrawerIndicatorEnabled(false); - } + mDrawerToggle.setDrawerIndicatorEnabled(chosenFile != null && isRoot(chosenFile)); } } @@ -700,7 +761,7 @@ private void setQuotaInformation(long usedSpace, long totalSpace, int relative, viewThemeUtils.material.colorProgressBar(mQuotaProgressBar); } else { viewThemeUtils.material.colorProgressBar(mQuotaProgressBar, - getResources().getColor(R.color.infolevel_warning)); + getResources().getColor(R.color.infolevel_warning, getTheme())); } updateQuotaLink(); @@ -1053,28 +1114,22 @@ public void showFiles(boolean onDeviceOnly) { @Override public void avatarGenerated(Drawable avatarDrawable, Object callContext) { - if (callContext instanceof MenuItem) { - MenuItem menuItem = (MenuItem) callContext; + if (callContext instanceof MenuItem menuItem) { menuItem.setIcon(avatarDrawable); - } else if (callContext instanceof ImageView) { - ImageView imageView = (ImageView) callContext; + } else if (callContext instanceof ImageView imageView) { imageView.setImageDrawable(avatarDrawable); - } else if (callContext instanceof MaterialButton) { - MaterialButton materialButton = (MaterialButton) callContext; + } else if (callContext instanceof MaterialButton materialButton) { materialButton.setIcon(avatarDrawable); } } @Override public boolean shouldCallGeneratedCallback(String tag, Object callContext) { - if (callContext instanceof MenuItem) { - MenuItem menuItem = (MenuItem) callContext; + if (callContext instanceof MenuItem menuItem) { return String.valueOf(menuItem.getTitle()).equals(tag); - } else if (callContext instanceof ImageView) { - ImageView imageView = (ImageView) callContext; + } else if (callContext instanceof ImageView imageView) { return String.valueOf(imageView.getTag()).equals(tag); - } else if (callContext instanceof MaterialButton) { - MaterialButton materialButton = (MaterialButton) callContext; + } else if (callContext instanceof MaterialButton materialButton) { return String.valueOf(materialButton.getTag()).equals(tag); } return false; diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index 9162852ef90e..effeefba5dd0 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -391,6 +391,10 @@ protected void onPostCreate(Bundle savedInstanceState) { if (OPEN_FILE.equals(getIntent().getAction())) { getSupportFragmentManager().executePendingTransactions(); onOpenFileIntent(getIntent()); + } else if (RESTART.equals(getIntent().getAction())) { + // most likely switched to different account + DisplayUtils.showSnackMessage(this, String.format(getString(R.string.logged_in_as), + accountManager.getUser().getAccountName())); } upgradeNotificationForInstantUpload(); @@ -2503,7 +2507,7 @@ private void handleOpenFileViaIntent(Intent intent) { openFileByPath(optionalUser.get(), filePath); } else { dismissLoadingDialog(); - DisplayUtils.showSnackMessage(this, getString(R.string.file_not_found)); + accountClicked(optionalUser.get().hashCode()); } } else { dismissLoadingDialog(); diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java index decd24ee2ed0..da23f3168a8c 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java @@ -137,6 +137,7 @@ public class SettingsActivity extends PreferenceActivity private ListPreference lock; private ThemeableSwitchPreference showHiddenFiles; + private ThemeableSwitchPreference showEcosystemApps; private AppCompatDelegate delegate; private ListPreference prefStoragePath; @@ -606,6 +607,7 @@ private void setupDetailsCategory(PreferenceScreen preferenceScreen) { boolean fPassCodeEnabled = getResources().getBoolean(R.bool.passcode_enabled); boolean fDeviceCredentialsEnabled = getResources().getBoolean(R.bool.device_credentials_enabled); boolean fShowHiddenFilesEnabled = getResources().getBoolean(R.bool.show_hidden_files_enabled); + boolean fShowEcosystemAppsEnabled = !getResources().getBoolean(R.bool.is_branded_client); boolean fSyncedFolderLightEnabled = getResources().getBoolean(R.bool.syncedFolder_light); boolean fShowMediaScanNotifications = preferences.isShowMediaScanNotifications(); @@ -613,6 +615,8 @@ private void setupDetailsCategory(PreferenceScreen preferenceScreen) { setupHiddenFilesPreference(preferenceCategoryDetails, fShowHiddenFilesEnabled); + setupShowEcosystemAppsPreference(preferenceCategoryDetails, fShowEcosystemAppsEnabled); + setupShowMediaScanNotifications(preferenceCategoryDetails, fShowMediaScanNotifications); if (!fPassCodeEnabled && !fDeviceCredentialsEnabled && !fShowHiddenFilesEnabled && fSyncedFolderLightEnabled @@ -644,6 +648,20 @@ private void setupHiddenFilesPreference(PreferenceCategory preferenceCategoryDet } } + private void setupShowEcosystemAppsPreference(PreferenceCategory preferenceCategoryDetails, + boolean fShowEcosystemAppsEnabled) { + showEcosystemApps = (ThemeableSwitchPreference) findPreference("show_ecosystem_apps"); + if (fShowEcosystemAppsEnabled) { + showEcosystemApps.setOnPreferenceClickListener(preference -> { + preferences.setShowEcosystemApps(showEcosystemApps.isChecked()); + return true; + }); + } else { + preferenceCategoryDetails.removePreference(showEcosystemApps); + } + } + + private void setupLockPreference(PreferenceCategory preferenceCategoryDetails, boolean passCodeEnabled, boolean deviceCredentialsEnabled) { diff --git a/app/src/main/res/drawable/ic_more_apps.xml b/app/src/main/res/drawable/ic_more_apps.xml new file mode 100644 index 000000000000..0f857b81eaef --- /dev/null +++ b/app/src/main/res/drawable/ic_more_apps.xml @@ -0,0 +1,38 @@ + + + + + + + diff --git a/app/src/main/res/drawable/ic_notes.xml b/app/src/main/res/drawable/ic_notes.xml new file mode 100644 index 000000000000..52386c660a46 --- /dev/null +++ b/app/src/main/res/drawable/ic_notes.xml @@ -0,0 +1,29 @@ + + + + diff --git a/app/src/main/res/drawable/white_outline.xml b/app/src/main/res/drawable/white_outline.xml new file mode 100644 index 000000000000..e45c7c1477a0 --- /dev/null +++ b/app/src/main/res/drawable/white_outline.xml @@ -0,0 +1,26 @@ + + + + diff --git a/app/src/main/res/layout/drawer_header.xml b/app/src/main/res/layout/drawer_header.xml index ecc396f1a1d4..8ec80c5da58f 100644 --- a/app/src/main/res/layout/drawer_header.xml +++ b/app/src/main/res/layout/drawer_header.xml @@ -1,5 +1,4 @@ - - + android:gravity="center" + android:orientation="vertical"> - - - + + + + + + + + + android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/standard_half_margin" + android:orientation="horizontal"> + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index f844b3156f33..d72d9d458f83 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -40,8 +40,7 @@ #EEEEEE #BDBDBD #666666 - - + #CC000000 #e9322d diff --git a/app/src/main/res/values/dims.xml b/app/src/main/res/values/dims.xml index f892bf4118fb..3805116abcbb 100644 --- a/app/src/main/res/values/dims.xml +++ b/app/src/main/res/values/dims.xml @@ -135,7 +135,6 @@ 18sp 24dp 4 - 130dp 12dp 50dp 10dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9c316680fb56..8fc1cbc3cd8b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -42,6 +42,8 @@ Device credentials enabled No device credentials have been set up. Show hidden files + Show app switcher + Nextcloud app suggestions in navigation heading Show media scan notifications Notify about newly found media folders Sync calendar & contacts @@ -853,6 +855,7 @@ Resharing is not allowed Retrieving fileā€¦ Associated account not found! + Logged in as %1$s Error retrieving file Could not retrieve URL Failed to pass file to download manager @@ -970,6 +973,7 @@ Data storage folder does not exist! This might be due to a backup restore on another device. Falling back to default. Please check settings to adjust data storage folder. Close + Login with %1$s to %2$s Login via direct link failed! The link to your %1$s web interface when you open it in the browser. Delayed due to too many wrong attempts @@ -1088,4 +1092,10 @@ Flip vertically Unable to edit image. edited + Nextcloud Notes + Nextcloud Talk + More Nextcloud Apps + Notes + Talk + More diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index f68a65dd1fa7..95018e9d87c8 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -50,9 +50,12 @@ android:dialogTitle="@string/prefs_lock_title" android:defaultValue="none"/> +