DrawerActivity.java 61 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489
  1. /*
  2. * Nextcloud Android client application
  3. *
  4. * @author Andy Scherzinger
  5. * @author Tobias Kaminsky
  6. * @author Chris Narkiewicz <hello@ezaquarii.com>
  7. * Copyright (C) 2016 Andy Scherzinger
  8. * Copyright (C) 2017 Tobias Kaminsky
  9. * Copyright (C) 2016 Nextcloud
  10. * Copyright (C) 2016 ownCloud Inc.
  11. * Copyright (C) 2020 Chris Narkiewicz <hello@ezaquarii.com>
  12. *
  13. * This program is free software; you can redistribute it and/or
  14. * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  15. * License as published by the Free Software Foundation; either
  16. * version 3 of the License, or any later version.
  17. *
  18. * This program is distributed in the hope that it will be useful,
  19. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  22. *
  23. * You should have received a copy of the GNU Affero General Public
  24. * License along with this program. If not, see <http://www.gnu.org/licenses/>.
  25. */
  26. package com.owncloud.android.ui.activity;
  27. import android.app.Activity;
  28. import android.content.Context;
  29. import android.content.Intent;
  30. import android.content.res.Configuration;
  31. import android.graphics.PorterDuff;
  32. import android.graphics.drawable.ColorDrawable;
  33. import android.graphics.drawable.Drawable;
  34. import android.graphics.drawable.LayerDrawable;
  35. import android.net.Uri;
  36. import android.os.Build;
  37. import android.os.Bundle;
  38. import android.os.Handler;
  39. import android.os.SystemClock;
  40. import android.text.Html;
  41. import android.text.TextUtils;
  42. import android.view.Menu;
  43. import android.view.MenuItem;
  44. import android.view.View;
  45. import android.view.ViewGroup;
  46. import android.webkit.URLUtil;
  47. import android.widget.ImageView;
  48. import android.widget.LinearLayout;
  49. import android.widget.ProgressBar;
  50. import android.widget.TextView;
  51. import com.bumptech.glide.Glide;
  52. import com.bumptech.glide.request.animation.GlideAnimation;
  53. import com.bumptech.glide.request.target.SimpleTarget;
  54. import com.google.android.material.navigation.NavigationView;
  55. import com.nextcloud.client.account.User;
  56. import com.nextcloud.client.di.Injectable;
  57. import com.nextcloud.client.network.ClientFactory;
  58. import com.nextcloud.client.onboarding.FirstRunActivity;
  59. import com.nextcloud.client.preferences.AppPreferences;
  60. import com.nextcloud.client.preferences.DarkMode;
  61. import com.nextcloud.java.util.Optional;
  62. import com.owncloud.android.MainApp;
  63. import com.owncloud.android.R;
  64. import com.owncloud.android.authentication.PassCodeManager;
  65. import com.owncloud.android.datamodel.ArbitraryDataProvider;
  66. import com.owncloud.android.datamodel.ExternalLinksProvider;
  67. import com.owncloud.android.datamodel.FileDataStorageManager;
  68. import com.owncloud.android.datamodel.OCFile;
  69. import com.owncloud.android.lib.common.ExternalLink;
  70. import com.owncloud.android.lib.common.ExternalLinkType;
  71. import com.owncloud.android.lib.common.Quota;
  72. import com.owncloud.android.lib.common.UserInfo;
  73. import com.owncloud.android.lib.common.accounts.ExternalLinksOperation;
  74. import com.owncloud.android.lib.common.operations.RemoteOperation;
  75. import com.owncloud.android.lib.common.operations.RemoteOperationResult;
  76. import com.owncloud.android.lib.common.utils.Log_OC;
  77. import com.owncloud.android.lib.resources.files.SearchRemoteOperation;
  78. import com.owncloud.android.lib.resources.status.CapabilityBooleanType;
  79. import com.owncloud.android.lib.resources.status.OCCapability;
  80. import com.owncloud.android.lib.resources.status.OwnCloudVersion;
  81. import com.owncloud.android.lib.resources.users.GetUserInfoRemoteOperation;
  82. import com.owncloud.android.operations.GetCapabilitiesOperation;
  83. import com.owncloud.android.ui.TextDrawable;
  84. import com.owncloud.android.ui.activities.ActivitiesActivity;
  85. import com.owncloud.android.ui.events.AccountRemovedEvent;
  86. import com.owncloud.android.ui.events.ChangeMenuEvent;
  87. import com.owncloud.android.ui.events.DummyDrawerEvent;
  88. import com.owncloud.android.ui.events.SearchEvent;
  89. import com.owncloud.android.ui.fragment.OCFileListFragment;
  90. import com.owncloud.android.ui.fragment.PhotoFragment;
  91. import com.owncloud.android.ui.trashbin.TrashbinActivity;
  92. import com.owncloud.android.utils.DisplayUtils;
  93. import com.owncloud.android.utils.DrawerMenuUtil;
  94. import com.owncloud.android.utils.FilesSyncHelper;
  95. import com.owncloud.android.utils.ThemeUtils;
  96. import com.owncloud.android.utils.svg.MenuSimpleTarget;
  97. import org.greenrobot.eventbus.EventBus;
  98. import org.greenrobot.eventbus.Subscribe;
  99. import org.greenrobot.eventbus.ThreadMode;
  100. import org.parceler.Parcels;
  101. import java.util.ArrayList;
  102. import java.util.Collections;
  103. import java.util.List;
  104. import javax.inject.Inject;
  105. import androidx.annotation.NonNull;
  106. import androidx.appcompat.app.ActionBarDrawerToggle;
  107. import androidx.appcompat.app.AppCompatDelegate;
  108. import androidx.core.content.ContextCompat;
  109. import androidx.core.view.GravityCompat;
  110. import androidx.drawerlayout.widget.DrawerLayout;
  111. import kotlin.collections.CollectionsKt;
  112. /**
  113. * Base class to handle setup of the drawer implementation including user switching and avatar fetching and fallback
  114. * generation.
  115. */
  116. public abstract class DrawerActivity extends ToolbarActivity
  117. implements DisplayUtils.AvatarGenerationListener, Injectable {
  118. private static final String TAG = DrawerActivity.class.getSimpleName();
  119. private static final String KEY_IS_ACCOUNT_CHOOSER_ACTIVE = "IS_ACCOUNT_CHOOSER_ACTIVE";
  120. private static final String KEY_CHECKED_MENU_ITEM = "CHECKED_MENU_ITEM";
  121. private static final int ACTION_MANAGE_ACCOUNTS = 101;
  122. private static final int MENU_ORDER_ACCOUNT = 1;
  123. private static final int MENU_ORDER_ACCOUNT_FUNCTION = 2;
  124. private static final int MENU_ORDER_EXTERNAL_LINKS = 3;
  125. private static final int MENU_ITEM_EXTERNAL_LINK = 111;
  126. /**
  127. * menu account avatar radius.
  128. */
  129. private float mMenuAccountAvatarRadiusDimension;
  130. /**
  131. * current account avatar radius.
  132. */
  133. private float mCurrentAccountAvatarRadiusDimension;
  134. /**
  135. * other accounts avatar radius.
  136. */
  137. private float mOtherAccountAvatarRadiusDimension;
  138. /**
  139. * Reference to the drawer layout.
  140. */
  141. protected DrawerLayout mDrawerLayout;
  142. /**
  143. * Reference to the drawer toggle.
  144. */
  145. protected ActionBarDrawerToggle mDrawerToggle;
  146. /**
  147. * Reference to the navigation view.
  148. */
  149. private NavigationView mNavigationView;
  150. /**
  151. * Reference to the account chooser toggle.
  152. */
  153. private ImageView mAccountChooserToggle;
  154. /**
  155. * Reference to the middle account avatar.
  156. */
  157. private ImageView mAccountMiddleAccountAvatar;
  158. /**
  159. * Reference to the end account avatar.
  160. */
  161. private ImageView mAccountEndAccountAvatar;
  162. /**
  163. * Flag to signal if the account chooser is active.
  164. */
  165. private boolean mIsAccountChooserActive;
  166. /**
  167. * Id of the checked menu item.
  168. */
  169. private int mCheckedMenuItem = Menu.NONE;
  170. /**
  171. * accounts for the (max) three displayed accounts in the drawer header.
  172. */
  173. private List<User> mAvatars = Collections.emptyList();
  174. /**
  175. * container layout of the quota view.
  176. */
  177. private LinearLayout mQuotaView;
  178. /**
  179. * progress bar of the quota view.
  180. */
  181. private ProgressBar mQuotaProgressBar;
  182. /**
  183. * text view of the quota view.
  184. */
  185. private TextView mQuotaTextPercentage;
  186. private TextView mQuotaTextLink;
  187. /**
  188. * runnable that will be executed after the drawer has been closed.
  189. */
  190. private Runnable pendingRunnable;
  191. private ExternalLinksProvider externalLinksProvider;
  192. private ArbitraryDataProvider arbitraryDataProvider;
  193. @Inject
  194. AppPreferences preferences;
  195. @Inject
  196. ClientFactory clientFactory;
  197. /**
  198. * Initializes the drawer, its content and highlights the menu item with the given id.
  199. * This method needs to be called after the content view has been set.
  200. *
  201. * @param menuItemId the menu item to be checked/highlighted
  202. */
  203. protected void setupDrawer(int menuItemId) {
  204. setupDrawer();
  205. setDrawerMenuItemChecked(menuItemId);
  206. }
  207. /**
  208. * Initializes the drawer and its content.
  209. * This method needs to be called after the content view has been set.
  210. */
  211. protected void setupDrawer() {
  212. mDrawerLayout = findViewById(R.id.drawer_layout);
  213. mNavigationView = findViewById(R.id.nav_view);
  214. if (mNavigationView != null) {
  215. setupDrawerHeader();
  216. setupDrawerMenu(mNavigationView);
  217. setupQuotaElement();
  218. }
  219. setupDrawerToggle();
  220. if (getSupportActionBar() != null) {
  221. getSupportActionBar().setDisplayHomeAsUpEnabled(true);
  222. }
  223. }
  224. /**
  225. * initializes and sets up the drawer toggle.
  226. */
  227. private void setupDrawerToggle() {
  228. mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.string.drawer_open, R.string.drawer_close) {
  229. /** Called when a drawer has settled in a completely closed state. */
  230. public void onDrawerClosed(View view) {
  231. super.onDrawerClosed(view);
  232. // standard behavior of drawer is to switch to the standard menu on closing
  233. if (mIsAccountChooserActive) {
  234. toggleAccountList();
  235. }
  236. supportInvalidateOptionsMenu();
  237. mDrawerToggle.setDrawerIndicatorEnabled(isDrawerIndicatorAvailable());
  238. if (pendingRunnable != null) {
  239. new Handler().post(pendingRunnable);
  240. pendingRunnable = null;
  241. }
  242. closeDrawer();
  243. }
  244. /** Called when a drawer has settled in a completely open state. */
  245. public void onDrawerOpened(View drawerView) {
  246. super.onDrawerOpened(drawerView);
  247. mDrawerToggle.setDrawerIndicatorEnabled(true);
  248. supportInvalidateOptionsMenu();
  249. }
  250. };
  251. // Set the drawer toggle as the DrawerListener
  252. mDrawerLayout.addDrawerListener(mDrawerToggle);
  253. mDrawerToggle.setDrawerIndicatorEnabled(true);
  254. mDrawerToggle.setDrawerSlideAnimationEnabled(true);
  255. }
  256. /**
  257. * initializes and sets up the drawer header.
  258. */
  259. private void setupDrawerHeader() {
  260. mAccountMiddleAccountAvatar = (ImageView) findNavigationViewChildById(R.id.drawer_account_middle);
  261. mAccountEndAccountAvatar = (ImageView) findNavigationViewChildById(R.id.drawer_account_end);
  262. mAccountChooserToggle = (ImageView) findNavigationViewChildById(R.id.drawer_account_chooser_toggle);
  263. mAccountChooserToggle.setColorFilter(ThemeUtils.fontColor(this, true));
  264. if (getResources().getBoolean(R.bool.allow_profile_click)) {
  265. mAccountChooserToggle.setImageResource(R.drawable.ic_down);
  266. findNavigationViewChildById(R.id.drawer_active_user)
  267. .setOnClickListener(new View.OnClickListener() {
  268. @Override
  269. public void onClick(View v) {
  270. toggleAccountList();
  271. }
  272. });
  273. } else {
  274. mAccountChooserToggle.setVisibility(View.GONE);
  275. }
  276. }
  277. /**
  278. * setup quota elements of the drawer.
  279. */
  280. private void setupQuotaElement() {
  281. mQuotaView = (LinearLayout) findQuotaViewById(R.id.drawer_quota);
  282. mQuotaProgressBar = (ProgressBar) findQuotaViewById(R.id.drawer_quota_ProgressBar);
  283. mQuotaTextPercentage = (TextView) findQuotaViewById(R.id.drawer_quota_percentage);
  284. mQuotaTextLink = (TextView) findQuotaViewById(R.id.drawer_quota_link);
  285. ThemeUtils.colorProgressBar(mQuotaProgressBar, ThemeUtils.primaryColor(this));
  286. }
  287. /**
  288. * setup drawer content, basically setting the item selected listener.
  289. *
  290. * @param navigationView the drawers navigation view
  291. */
  292. protected void setupDrawerMenu(NavigationView navigationView) {
  293. navigationView.setItemIconTintList(null);
  294. // setup actions for drawer menu items
  295. navigationView.setNavigationItemSelectedListener(
  296. new NavigationView.OnNavigationItemSelectedListener() {
  297. @Override
  298. public boolean onNavigationItemSelected(@NonNull final MenuItem menuItem) {
  299. mDrawerLayout.closeDrawers();
  300. // pending runnable will be executed after the drawer has been closed
  301. pendingRunnable = new Runnable() {
  302. @Override
  303. public void run() {
  304. selectNavigationItem(menuItem);
  305. }
  306. };
  307. return true;
  308. }
  309. });
  310. // handle correct state
  311. if (mIsAccountChooserActive) {
  312. navigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, true);
  313. } else {
  314. navigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, false);
  315. }
  316. User account = accountManager.getUser();
  317. filterDrawerMenu(navigationView.getMenu(), account);
  318. }
  319. private void filterDrawerMenu(final Menu menu, @NonNull final User user) {
  320. FileDataStorageManager storageManager = new FileDataStorageManager(user.toPlatformAccount(),
  321. getContentResolver());
  322. OCCapability capability = storageManager.getCapability(user.getAccountName());
  323. DrawerMenuUtil.filterSearchMenuItems(menu, user, getResources(), true);
  324. DrawerMenuUtil.filterTrashbinMenuItem(menu, user, capability);
  325. DrawerMenuUtil.filterActivityMenuItem(menu, capability);
  326. DrawerMenuUtil.setupHomeMenuItem(menu, getResources());
  327. DrawerMenuUtil.removeMenuItem(menu, R.id.nav_community,
  328. !getResources().getBoolean(R.bool.participate_enabled));
  329. DrawerMenuUtil.removeMenuItem(menu, R.id.nav_shared, !getResources().getBoolean(R.bool.shared_enabled));
  330. DrawerMenuUtil.removeMenuItem(menu, R.id.nav_contacts, !getResources().getBoolean(R.bool.contacts_backup)
  331. || !getResources().getBoolean(R.bool.show_drawer_contacts_backup));
  332. DrawerMenuUtil.removeMenuItem(menu, R.id.nav_synced_folders,
  333. getResources().getBoolean(R.bool.syncedFolder_light));
  334. DrawerMenuUtil.removeMenuItem(menu, R.id.nav_logout, !getResources().getBoolean(R.bool.show_drawer_logout));
  335. }
  336. @Subscribe(threadMode = ThreadMode.MAIN)
  337. public void onMessageEvent(DummyDrawerEvent event) {
  338. unsetAllDrawerMenuItems();
  339. }
  340. private void selectNavigationItem(final MenuItem menuItem) {
  341. setDrawerMenuItemChecked(menuItem.getItemId());
  342. if (menuItem.getGroupId() == R.id.drawer_menu_accounts) {
  343. handleAccountItemClick(menuItem);
  344. return;
  345. }
  346. switch (menuItem.getItemId()) {
  347. case R.id.nav_all_files:
  348. if (this instanceof FileDisplayActivity) {
  349. if (((FileDisplayActivity) this).getListOfFilesFragment() instanceof PhotoFragment) {
  350. Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class);
  351. intent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItem.getItemId());
  352. intent.setAction(FileDisplayActivity.ALL_FILES);
  353. intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
  354. startActivity(intent);
  355. } else {
  356. ((FileDisplayActivity) this).browseToRoot();
  357. EventBus.getDefault().post(new ChangeMenuEvent());
  358. }
  359. } else {
  360. showFiles(false);
  361. Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class);
  362. intent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItem.getItemId());
  363. intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
  364. startActivity(intent);
  365. }
  366. break;
  367. case R.id.nav_favorites:
  368. handleSearchEvents(new SearchEvent("", SearchRemoteOperation.SearchType.FAVORITE_SEARCH),
  369. menuItem.getItemId());
  370. break;
  371. case R.id.nav_photos:
  372. startPhotoSearch(menuItem);
  373. break;
  374. case R.id.nav_on_device:
  375. EventBus.getDefault().post(new ChangeMenuEvent());
  376. showFiles(true);
  377. break;
  378. case R.id.nav_uploads:
  379. startActivity(UploadListActivity.class, Intent.FLAG_ACTIVITY_CLEAR_TOP);
  380. break;
  381. case R.id.nav_trashbin:
  382. startActivity(TrashbinActivity.class, Intent.FLAG_ACTIVITY_CLEAR_TOP);
  383. break;
  384. case R.id.nav_activity:
  385. startActivity(ActivitiesActivity.class, Intent.FLAG_ACTIVITY_CLEAR_TOP);
  386. break;
  387. case R.id.nav_notifications:
  388. startActivity(NotificationsActivity.class);
  389. break;
  390. case R.id.nav_synced_folders:
  391. startActivity(SyncedFoldersActivity.class);
  392. break;
  393. case R.id.nav_contacts:
  394. startActivity(ContactsPreferenceActivity.class);
  395. break;
  396. case R.id.nav_settings:
  397. startActivity(SettingsActivity.class);
  398. break;
  399. case R.id.nav_community:
  400. startActivity(CommunityActivity.class);
  401. break;
  402. case R.id.nav_logout:
  403. mCheckedMenuItem = -1;
  404. menuItem.setChecked(false);
  405. final Optional<User> optionalUser = getUser();
  406. if (optionalUser.isPresent()) {
  407. UserInfoActivity.openAccountRemovalConfirmationDialog(optionalUser.get(), getSupportFragmentManager());
  408. }
  409. break;
  410. case R.id.nav_shared:
  411. handleSearchEvents(new SearchEvent("", SearchRemoteOperation.SearchType.SHARED_FILTER),
  412. menuItem.getItemId());
  413. break;
  414. case R.id.nav_recently_modified:
  415. handleSearchEvents(new SearchEvent("", SearchRemoteOperation.SearchType.RECENTLY_MODIFIED_SEARCH),
  416. menuItem.getItemId());
  417. break;
  418. default:
  419. if (menuItem.getItemId() >= MENU_ITEM_EXTERNAL_LINK &&
  420. menuItem.getItemId() <= MENU_ITEM_EXTERNAL_LINK + 100) {
  421. // external link clicked
  422. externalLinkClicked(menuItem);
  423. } else {
  424. Log_OC.i(TAG, "Unknown drawer menu item clicked: " + menuItem.getTitle());
  425. }
  426. break;
  427. }
  428. }
  429. private void startActivity(Class<? extends Activity> activity) {
  430. startActivity(new Intent(getApplicationContext(), activity));
  431. }
  432. private void startActivity(Class<? extends Activity> activity, int flags) {
  433. Intent intent = new Intent(getApplicationContext(), activity);
  434. intent.setFlags(flags);
  435. startActivity(intent);
  436. }
  437. private void handleAccountItemClick(MenuItem menuItem) {
  438. switch (menuItem.getItemId()) {
  439. case R.id.drawer_menu_account_add:
  440. boolean isProviderOrOwnInstallationVisible = getResources()
  441. .getBoolean(R.bool.show_provider_or_own_installation);
  442. if (isProviderOrOwnInstallationVisible) {
  443. Intent firstRunIntent = new Intent(getApplicationContext(), FirstRunActivity.class);
  444. firstRunIntent.putExtra(FirstRunActivity.EXTRA_ALLOW_CLOSE, true);
  445. startActivity(firstRunIntent);
  446. } else {
  447. startAccountCreation();
  448. }
  449. break;
  450. case R.id.drawer_menu_account_manage:
  451. Intent manageAccountsIntent = new Intent(getApplicationContext(), ManageAccountsActivity.class);
  452. startActivityForResult(manageAccountsIntent, ACTION_MANAGE_ACCOUNTS);
  453. break;
  454. default:
  455. accountClicked(menuItem.getItemId());
  456. break;
  457. }
  458. }
  459. private void startPhotoSearch(MenuItem menuItem) {
  460. SearchEvent searchEvent = new SearchEvent("image/%", SearchRemoteOperation.SearchType.PHOTO_SEARCH);
  461. Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class);
  462. intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
  463. intent.setAction(Intent.ACTION_SEARCH);
  464. intent.putExtra(OCFileListFragment.SEARCH_EVENT, Parcels.wrap(searchEvent));
  465. intent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItem.getItemId());
  466. startActivity(intent);
  467. }
  468. private void handleSearchEvents(SearchEvent searchEvent, int menuItemId) {
  469. if (this instanceof FileDisplayActivity) {
  470. if (((FileDisplayActivity) this).getListOfFilesFragment() instanceof PhotoFragment) {
  471. Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class);
  472. intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
  473. intent.setAction(Intent.ACTION_SEARCH);
  474. intent.putExtra(OCFileListFragment.SEARCH_EVENT, Parcels.wrap(searchEvent));
  475. intent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItemId);
  476. startActivity(intent);
  477. } else {
  478. EventBus.getDefault().post(searchEvent);
  479. }
  480. } else {
  481. Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class);
  482. intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
  483. intent.setAction(Intent.ACTION_SEARCH);
  484. intent.putExtra(OCFileListFragment.SEARCH_EVENT, Parcels.wrap(searchEvent));
  485. intent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItemId);
  486. startActivity(intent);
  487. }
  488. }
  489. /**
  490. * show the file list to the user.
  491. *
  492. * @param onDeviceOnly flag to decide if all files or only the ones on the device should be shown
  493. */
  494. public abstract void showFiles(boolean onDeviceOnly);
  495. /**
  496. * sets the new/current account and restarts. In case the given account equals the actual/current account the
  497. * call will be ignored.
  498. *
  499. * @param hashCode HashCode of account to be set
  500. */
  501. private void accountClicked(int hashCode) {
  502. final User currentUser = accountManager.getUser();
  503. if (currentUser.hashCode() != hashCode && accountManager.setCurrentOwnCloudAccount(hashCode)) {
  504. fetchExternalLinks(true);
  505. restart();
  506. }
  507. }
  508. private void externalLinkClicked(MenuItem menuItem){
  509. for (ExternalLink link : externalLinksProvider.getExternalLink(ExternalLinkType.LINK)) {
  510. if (menuItem.getTitle().toString().equalsIgnoreCase(link.name)) {
  511. if (link.redirect) {
  512. Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(link.url));
  513. DisplayUtils.startIntentIfAppAvailable(intent, this, R.string.no_browser_available);
  514. } else {
  515. Intent externalWebViewIntent = new Intent(getApplicationContext(), ExternalSiteWebView.class);
  516. externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_TITLE, link.name);
  517. externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_URL, link.url);
  518. externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, true);
  519. externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_MENU_ITEM_ID, menuItem.getItemId());
  520. startActivity(externalWebViewIntent);
  521. }
  522. }
  523. }
  524. }
  525. /**
  526. * click method for mini avatars in drawer header.
  527. *
  528. * @param view the clicked ImageView
  529. */
  530. public void onAccountDrawerClick(View view) {
  531. accountClicked((int) view.getTag());
  532. }
  533. /**
  534. * checks if the drawer exists and is opened.
  535. *
  536. * @return <code>true</code> if the drawer is open, else <code>false</code>
  537. */
  538. public boolean isDrawerOpen() {
  539. return mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START);
  540. }
  541. /**
  542. * closes the drawer.
  543. */
  544. public void closeDrawer() {
  545. if (mDrawerLayout != null) {
  546. mDrawerLayout.closeDrawer(GravityCompat.START);
  547. }
  548. }
  549. /**
  550. * opens the drawer.
  551. */
  552. public void openDrawer() {
  553. if (mDrawerLayout != null) {
  554. mDrawerLayout.openDrawer(GravityCompat.START);
  555. updateExternalLinksInDrawer();
  556. updateQuotaLink();
  557. }
  558. }
  559. /**
  560. * Enable or disable interaction with all drawers.
  561. *
  562. * @param lockMode The new lock mode for the given drawer. One of {@link DrawerLayout#LOCK_MODE_UNLOCKED},
  563. * {@link DrawerLayout#LOCK_MODE_LOCKED_CLOSED} or {@link DrawerLayout#LOCK_MODE_LOCKED_OPEN}.
  564. */
  565. public void setDrawerLockMode(int lockMode) {
  566. if (mDrawerLayout != null) {
  567. mDrawerLayout.setDrawerLockMode(lockMode);
  568. }
  569. }
  570. /**
  571. * Enable or disable the drawer indicator.
  572. *
  573. * @param enable true to enable, false to disable
  574. */
  575. public void setDrawerIndicatorEnabled(boolean enable) {
  576. if (mDrawerToggle != null) {
  577. mDrawerToggle.setDrawerIndicatorEnabled(enable);
  578. }
  579. }
  580. /**
  581. * updates the account list in the drawer.
  582. */
  583. public void updateAccountList() {
  584. List<User> users = accountManager.getAllUsers();
  585. ArrayList<User> persistingAccounts = new ArrayList<>();
  586. for (User user: users) {
  587. boolean pendingForRemoval = arbitraryDataProvider.getBooleanValue(user.toPlatformAccount(),
  588. ManageAccountsActivity.PENDING_FOR_REMOVAL);
  589. if (!pendingForRemoval) {
  590. persistingAccounts.add(user);
  591. }
  592. }
  593. if (mNavigationView != null && mDrawerLayout != null) {
  594. if (persistingAccounts.size() > 0) {
  595. repopulateAccountList(persistingAccounts);
  596. setAccountInDrawer(accountManager.getUser());
  597. mAvatars = getUserAvatars();
  598. // activate second/end account avatar
  599. final User secondUser = mAvatars.size() > 1 ? mAvatars.get(1) : null;
  600. if (secondUser != null) {
  601. mAccountEndAccountAvatar.setTag(secondUser.hashCode());
  602. DisplayUtils.setAvatar(secondUser,
  603. this,
  604. mOtherAccountAvatarRadiusDimension,
  605. getResources(),
  606. mAccountEndAccountAvatar,
  607. this);
  608. mAccountEndAccountAvatar.setVisibility(View.VISIBLE);
  609. } else {
  610. mAccountEndAccountAvatar.setVisibility(View.GONE);
  611. }
  612. // activate third/middle account avatar
  613. final User thirdUser = mAvatars.size() > 2 ? mAvatars.get(2) : null;
  614. if (thirdUser != null) {
  615. mAccountMiddleAccountAvatar.setTag(thirdUser.hashCode());
  616. DisplayUtils.setAvatar(thirdUser,
  617. this,
  618. mOtherAccountAvatarRadiusDimension,
  619. getResources(),
  620. mAccountMiddleAccountAvatar,
  621. this);
  622. mAccountMiddleAccountAvatar.setVisibility(View.VISIBLE);
  623. } else {
  624. mAccountMiddleAccountAvatar.setVisibility(View.GONE);
  625. }
  626. } else {
  627. mAccountEndAccountAvatar.setVisibility(View.GONE);
  628. mAccountMiddleAccountAvatar.setVisibility(View.GONE);
  629. }
  630. }
  631. }
  632. /**
  633. * re-populates the account list.
  634. *
  635. * @param users list of users
  636. */
  637. private void repopulateAccountList(List<User> users) {
  638. // remove all accounts from list
  639. mNavigationView.getMenu().removeGroup(R.id.drawer_menu_accounts);
  640. // add all accounts to list
  641. for (User user: users) {
  642. try {
  643. // show all accounts except the currently active one and those pending for removal
  644. if (!getAccount().name.equals(user.getAccountName())) {
  645. MenuItem accountMenuItem = mNavigationView.getMenu().add(
  646. R.id.drawer_menu_accounts,
  647. user.hashCode(),
  648. MENU_ORDER_ACCOUNT,
  649. DisplayUtils.getAccountNameDisplayText(user))
  650. .setIcon(TextDrawable.createAvatar(user.toPlatformAccount(),
  651. mMenuAccountAvatarRadiusDimension));
  652. DisplayUtils.setAvatar(user, this, mMenuAccountAvatarRadiusDimension, getResources(),
  653. accountMenuItem, this);
  654. }
  655. } catch (Exception e) {
  656. Log_OC.e(TAG, "Error calculating RGB value for account menu item.", e);
  657. mNavigationView.getMenu().add(
  658. R.id.drawer_menu_accounts,
  659. user.hashCode(),
  660. MENU_ORDER_ACCOUNT,
  661. DisplayUtils.getAccountNameDisplayText(user))
  662. .setIcon(R.drawable.ic_user);
  663. }
  664. }
  665. // re-add add-account and manage-accounts
  666. mNavigationView.getMenu().add(R.id.drawer_menu_accounts, R.id.drawer_menu_account_add,
  667. MENU_ORDER_ACCOUNT_FUNCTION,
  668. getResources().getString(R.string.prefs_add_account)).setIcon(R.drawable.ic_account_plus);
  669. mNavigationView.getMenu().add(R.id.drawer_menu_accounts, R.id.drawer_menu_account_manage,
  670. MENU_ORDER_ACCOUNT_FUNCTION,
  671. getResources().getString(R.string.drawer_manage_accounts)).setIcon(R.drawable.nav_settings);
  672. // adding sets menu group back to visible, so safety check and setting invisible
  673. showMenu();
  674. }
  675. /**
  676. * Updates title bar and home buttons (state and icon).
  677. * Assumes that navigation drawer is NOT visible.
  678. */
  679. protected void updateActionBarTitleAndHomeButton(OCFile chosenFile) {
  680. super.updateActionBarTitleAndHomeButton(chosenFile);
  681. // set home button properties
  682. if (mDrawerToggle != null && chosenFile != null) {
  683. if (isRoot(chosenFile)) {
  684. mDrawerToggle.setDrawerIndicatorEnabled(true);
  685. } else {
  686. mDrawerToggle.setDrawerIndicatorEnabled(false);
  687. Drawable upArrow = getResources().getDrawable(R.drawable.ic_arrow_back);
  688. upArrow.setColorFilter(ThemeUtils.fontColor(this), PorterDuff.Mode.SRC_ATOP);
  689. mDrawerToggle.setHomeAsUpIndicator(upArrow);
  690. }
  691. } else if (mDrawerToggle != null) {
  692. mDrawerToggle.setDrawerIndicatorEnabled(false);
  693. }
  694. }
  695. /**
  696. * sets the given account name in the drawer in case the drawer is available. The account name is shortened
  697. * beginning from the @-sign in the username.
  698. *
  699. * @param user the account to be set in the drawer
  700. */
  701. protected void setAccountInDrawer(User user) {
  702. if (mDrawerLayout != null && user != null) {
  703. TextView username = (TextView) findNavigationViewChildById(R.id.drawer_username);
  704. TextView usernameFull = (TextView) findNavigationViewChildById(R.id.drawer_username_full);
  705. String name = user.getAccountName();
  706. usernameFull.setText(DisplayUtils.convertIdn(name.substring(name.lastIndexOf('@') + 1),
  707. false));
  708. usernameFull.setTextColor(ThemeUtils.fontColor(this));
  709. username.setText(user.toOwnCloudAccount().getDisplayName());
  710. username.setTextColor(ThemeUtils.fontColor(this));
  711. View currentAccountView = findNavigationViewChildById(R.id.drawer_current_account);
  712. currentAccountView.setTag(name);
  713. DisplayUtils.setAvatar(user, this, mCurrentAccountAvatarRadiusDimension, getResources(),
  714. currentAccountView, this);
  715. // check and show quota info if available
  716. getAndDisplayUserQuota();
  717. }
  718. }
  719. /**
  720. * Toggle between standard menu and account list including saving the state.
  721. */
  722. private void toggleAccountList() {
  723. mIsAccountChooserActive = !mIsAccountChooserActive;
  724. showMenu();
  725. }
  726. /**
  727. * depending on the #mIsAccountChooserActive flag shows the account chooser or the standard menu.
  728. */
  729. private void showMenu() {
  730. if (mNavigationView != null) {
  731. if (mIsAccountChooserActive) {
  732. if (mAccountChooserToggle != null) {
  733. mAccountChooserToggle.setImageResource(R.drawable.ic_up);
  734. }
  735. mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, true);
  736. if (!getResources().getBoolean(R.bool.multiaccount_support) &&
  737. mNavigationView.getMenu().findItem(R.id.drawer_menu_account_add) != null) {
  738. mNavigationView.getMenu().removeItem(R.id.drawer_menu_account_add);
  739. }
  740. mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_standard, false);
  741. mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_external_links, false);
  742. mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_bottom, false);
  743. } else {
  744. if (mAccountChooserToggle != null) {
  745. mAccountChooserToggle.setImageResource(R.drawable.ic_down);
  746. }
  747. mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, false);
  748. mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_standard, true);
  749. mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_external_links, true);
  750. mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_bottom, true);
  751. }
  752. }
  753. }
  754. /**
  755. * shows or hides the quota UI elements.
  756. *
  757. * @param showQuota show/hide quota information
  758. */
  759. private void showQuota(boolean showQuota) {
  760. if (showQuota) {
  761. mQuotaView.setVisibility(View.VISIBLE);
  762. } else {
  763. mQuotaView.setVisibility(View.GONE);
  764. }
  765. }
  766. /**
  767. * configured the quota to be displayed.
  768. * @param usedSpace the used space
  769. * @param totalSpace the total space
  770. * @param relative the percentage of space already used
  771. * @param quotaValue {@link GetUserInfoRemoteOperation#SPACE_UNLIMITED} or other to determinate state
  772. */
  773. private void setQuotaInformation(long usedSpace, long totalSpace, int relative, long quotaValue) {
  774. if (GetUserInfoRemoteOperation.SPACE_UNLIMITED == quotaValue) {
  775. mQuotaTextPercentage.setText(String.format(
  776. getString(R.string.drawer_quota_unlimited),
  777. DisplayUtils.bytesToHumanReadable(usedSpace)));
  778. } else {
  779. mQuotaTextPercentage.setText(String.format(
  780. getString(R.string.drawer_quota),
  781. DisplayUtils.bytesToHumanReadable(usedSpace),
  782. DisplayUtils.bytesToHumanReadable(totalSpace)));
  783. }
  784. mQuotaProgressBar.setProgress(relative);
  785. ThemeUtils.colorProgressBar(mQuotaProgressBar, DisplayUtils.getRelativeInfoColor(this, relative));
  786. updateQuotaLink();
  787. showQuota(true);
  788. }
  789. protected void unsetAllDrawerMenuItems() {
  790. if (mNavigationView != null && mNavigationView.getMenu() != null) {
  791. Menu menu = mNavigationView.getMenu();
  792. for (int i = 0; i < menu.size(); i++) {
  793. menu.getItem(i).setChecked(false);
  794. }
  795. }
  796. mCheckedMenuItem = Menu.NONE;
  797. }
  798. private void updateQuotaLink() {
  799. if (mQuotaTextLink != null) {
  800. if (getBaseContext().getResources().getBoolean(R.bool.show_external_links)) {
  801. List<ExternalLink> quotas = externalLinksProvider.getExternalLink(ExternalLinkType.QUOTA);
  802. float density = getResources().getDisplayMetrics().density;
  803. final int size = Math.round(24 * density);
  804. if (quotas.size() > 0) {
  805. final ExternalLink firstQuota = quotas.get(0);
  806. mQuotaTextLink.setText(firstQuota.name);
  807. mQuotaTextLink.setClickable(true);
  808. mQuotaTextLink.setVisibility(View.VISIBLE);
  809. mQuotaTextLink.setOnClickListener(new View.OnClickListener() {
  810. @Override
  811. public void onClick(View v) {
  812. Intent externalWebViewIntent = new Intent(getApplicationContext(), ExternalSiteWebView.class);
  813. externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_TITLE, firstQuota.name);
  814. externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_URL, firstQuota.url);
  815. externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, true);
  816. externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_MENU_ITEM_ID, -1);
  817. startActivity(externalWebViewIntent);
  818. }
  819. });
  820. SimpleTarget target = new SimpleTarget<Drawable>() {
  821. @Override
  822. public void onResourceReady(Drawable resource, GlideAnimation glideAnimation) {
  823. Drawable test = resource.getCurrent();
  824. test.setBounds(0, 0, size, size);
  825. mQuotaTextLink.setCompoundDrawablesWithIntrinsicBounds(test, null, null, null);
  826. }
  827. @Override
  828. public void onLoadFailed(Exception e, Drawable errorDrawable) {
  829. super.onLoadFailed(e, errorDrawable);
  830. Drawable test = errorDrawable.getCurrent();
  831. test.setBounds(0, 0, size, size);
  832. mQuotaTextLink.setCompoundDrawablesWithIntrinsicBounds(test, null, null, null);
  833. }
  834. };
  835. DisplayUtils.downloadIcon(getUserAccountManager(),
  836. clientFactory,
  837. this,
  838. firstQuota.iconUrl,
  839. target,
  840. R.drawable.ic_link,
  841. size,
  842. size);
  843. } else {
  844. mQuotaTextLink.setVisibility(View.GONE);
  845. }
  846. } else {
  847. mQuotaTextLink.setVisibility(View.GONE);
  848. }
  849. }
  850. }
  851. /**
  852. * checks/highlights the provided menu item if the drawer has been initialized and the menu item exists.
  853. *
  854. * @param menuItemId the menu item to be highlighted
  855. */
  856. protected void setDrawerMenuItemChecked(int menuItemId) {
  857. if (mNavigationView != null && mNavigationView.getMenu() != null &&
  858. mNavigationView.getMenu().findItem(menuItemId) != null) {
  859. MenuItem item = mNavigationView.getMenu().findItem(menuItemId);
  860. item.setChecked(true);
  861. // reset all tinted icons
  862. for (int i = 0; i < mNavigationView.getMenu().size(); i++) {
  863. MenuItem menuItem = mNavigationView.getMenu().getItem(i);
  864. if (menuItem.getIcon() != null) {
  865. menuItem.getIcon().clearColorFilter();
  866. if (menuItem.getGroupId() != R.id.drawer_menu_accounts
  867. || menuItem.getItemId() == R.id.drawer_menu_account_add
  868. || menuItem.getItemId() == R.id.drawer_menu_account_manage) {
  869. ThemeUtils.tintDrawable(
  870. menuItem.getIcon(), ContextCompat.getColor(this, R.color.drawer_menu_icon));
  871. }
  872. menuItem.setTitle(Html.fromHtml(
  873. "<font color='"
  874. + ThemeUtils.colorToHexString(ContextCompat.getColor(this, R.color.text_color))
  875. + "'>" + menuItem.getTitle()
  876. + "</font>"));
  877. }
  878. }
  879. int elementColor = ThemeUtils.elementColor(this);
  880. ThemeUtils.tintDrawable(item.getIcon(), elementColor);
  881. String colorHex = ThemeUtils.colorToHexString(elementColor);
  882. item.setTitle(Html.fromHtml("<font color='" + colorHex + "'>" + item.getTitle() + "</font>"));
  883. mCheckedMenuItem = menuItemId;
  884. } else {
  885. Log_OC.w(TAG, "setDrawerMenuItemChecked has been called with invalid menu-item-ID");
  886. }
  887. }
  888. /**
  889. * Retrieves and shows the user quota if available
  890. */
  891. private void getAndDisplayUserQuota() {
  892. // set user space information
  893. Thread t = new Thread(new Runnable() {
  894. public void run() {
  895. final User user = accountManager.getUser();
  896. if (user.isAnonymous()) {
  897. return;
  898. }
  899. final Context context = MainApp.getAppContext();
  900. RemoteOperationResult result = new GetUserInfoRemoteOperation().execute(user.toPlatformAccount(), context);
  901. if (result.isSuccess() && result.getData() != null) {
  902. final UserInfo userInfo = (UserInfo) result.getData().get(0);
  903. final Quota quota = userInfo.getQuota();
  904. if (quota != null) {
  905. final long used = quota.getUsed();
  906. final long total = quota.getTotal();
  907. final int relative = (int) Math.ceil(quota.getRelative());
  908. final long quotaValue = quota.getQuota();
  909. runOnUiThread(new Runnable() {
  910. @Override
  911. public void run() {
  912. if (quotaValue > 0 || quotaValue == GetUserInfoRemoteOperation.SPACE_UNLIMITED
  913. || quotaValue == GetUserInfoRemoteOperation.QUOTA_LIMIT_INFO_NOT_AVAILABLE) {
  914. /*
  915. * show quota in case
  916. * it is available and calculated (> 0) or
  917. * in case of legacy servers (==QUOTA_LIMIT_INFO_NOT_AVAILABLE)
  918. */
  919. setQuotaInformation(used, total, relative, quotaValue);
  920. } else {
  921. /*
  922. * quotaValue < 0 means special cases like
  923. * {@link RemoteGetUserQuotaOperation.SPACE_NOT_COMPUTED},
  924. * {@link RemoteGetUserQuotaOperation.SPACE_UNKNOWN} or
  925. * {@link RemoteGetUserQuotaOperation.SPACE_UNLIMITED}
  926. * thus don't display any quota information.
  927. */
  928. showQuota(false);
  929. }
  930. }
  931. });
  932. }
  933. }
  934. }
  935. });
  936. t.start();
  937. }
  938. public void updateExternalLinksInDrawer() {
  939. if (mNavigationView != null && getBaseContext().getResources().getBoolean(R.bool.show_external_links)) {
  940. mNavigationView.getMenu().removeGroup(R.id.drawer_menu_external_links);
  941. float density = getResources().getDisplayMetrics().density;
  942. final int size = Math.round(24 * density);
  943. int greyColor = ContextCompat.getColor(this, R.color.drawer_menu_icon);
  944. for (final ExternalLink link : externalLinksProvider.getExternalLink(ExternalLinkType.LINK)) {
  945. int id = mNavigationView.getMenu().add(R.id.drawer_menu_external_links,
  946. MENU_ITEM_EXTERNAL_LINK + link.id, MENU_ORDER_EXTERNAL_LINKS, link.name)
  947. .setCheckable(true).getItemId();
  948. MenuSimpleTarget target = new MenuSimpleTarget<Drawable>(id) {
  949. @Override
  950. public void onResourceReady(Drawable resource, GlideAnimation glideAnimation) {
  951. setExternalLinkIcon(getIdMenuItem(), resource, greyColor);
  952. }
  953. @Override
  954. public void onLoadFailed(Exception e, Drawable errorDrawable) {
  955. super.onLoadFailed(e, errorDrawable);
  956. setExternalLinkIcon(getIdMenuItem(), errorDrawable, greyColor);
  957. }
  958. };
  959. DisplayUtils.downloadIcon(getUserAccountManager(),
  960. clientFactory,
  961. this,
  962. link.iconUrl,
  963. target,
  964. R.drawable.ic_link,
  965. size,
  966. size);
  967. }
  968. setDrawerMenuItemChecked(mCheckedMenuItem);
  969. }
  970. }
  971. private void setExternalLinkIcon(int id, Drawable drawable, int greyColor) {
  972. MenuItem menuItem = mNavigationView.getMenu().findItem(id);
  973. if (menuItem != null) {
  974. if (drawable != null) {
  975. menuItem.setIcon(ThemeUtils.tintDrawable(drawable, greyColor));
  976. } else {
  977. menuItem.setIcon(R.drawable.ic_link);
  978. }
  979. }
  980. }
  981. public void updateHeaderBackground() {
  982. if (getAccount() != null &&
  983. getStorageManager().getCapability(getAccount().name).getServerBackground() != null) {
  984. final ViewGroup navigationHeader = (ViewGroup) findNavigationViewChildById(R.id.drawer_header_view);
  985. if (navigationHeader != null) {
  986. OCCapability capability = getStorageManager().getCapability(getAccount().name);
  987. String background = capability.getServerBackground();
  988. CapabilityBooleanType backgroundDefault = capability.getServerBackgroundDefault();
  989. CapabilityBooleanType backgroundPlain = capability.getServerBackgroundPlain();
  990. int primaryColor = ThemeUtils.primaryColor(getAccount(), false, this);
  991. if (backgroundDefault.isTrue() && backgroundPlain.isTrue()) {
  992. // use only solid color
  993. setNavigationHeaderBackground(new ColorDrawable(primaryColor), navigationHeader);
  994. } else if (backgroundDefault.isTrue() && backgroundPlain.isFalse()) {
  995. // use nc13 background image with themed color
  996. Drawable[] drawables = {new ColorDrawable(primaryColor),
  997. getResources().getDrawable(R.drawable.background)};
  998. LayerDrawable layerDrawable = new LayerDrawable(drawables);
  999. setNavigationHeaderBackground(layerDrawable, navigationHeader);
  1000. } else {
  1001. // use url
  1002. if (URLUtil.isValidUrl(background) || background.isEmpty()) {
  1003. // background image
  1004. SimpleTarget target = new SimpleTarget<Drawable>() {
  1005. @Override
  1006. public void onResourceReady(Drawable resource, GlideAnimation glideAnimation) {
  1007. Drawable[] drawables = {new ColorDrawable(primaryColor), resource};
  1008. LayerDrawable layerDrawable = new LayerDrawable(drawables);
  1009. setNavigationHeaderBackground(layerDrawable, navigationHeader);
  1010. }
  1011. @Override
  1012. public void onLoadFailed(Exception e, Drawable errorDrawable) {
  1013. Drawable[] drawables = {new ColorDrawable(primaryColor), errorDrawable};
  1014. LayerDrawable layerDrawable = new LayerDrawable(drawables);
  1015. setNavigationHeaderBackground(layerDrawable, navigationHeader);
  1016. }
  1017. };
  1018. int backgroundResource;
  1019. OwnCloudVersion ownCloudVersion = accountManager.getServerVersion(getAccount());
  1020. if (ownCloudVersion.compareTo(OwnCloudVersion.nextcloud_18) >= 0) {
  1021. backgroundResource = R.drawable.background_nc18;
  1022. } else {
  1023. backgroundResource = R.drawable.background;
  1024. }
  1025. Glide.with(this)
  1026. .load(background)
  1027. .centerCrop()
  1028. .placeholder(backgroundResource)
  1029. .error(backgroundResource)
  1030. .crossFade()
  1031. .into(target);
  1032. } else {
  1033. // plain color
  1034. setNavigationHeaderBackground(new ColorDrawable(primaryColor), navigationHeader);
  1035. }
  1036. }
  1037. }
  1038. }
  1039. }
  1040. private void setNavigationHeaderBackground(Drawable drawable, ViewGroup navigationHeader) {
  1041. final ImageView background = navigationHeader.findViewById(R.id.drawer_header_background);
  1042. background.setImageDrawable(drawable);
  1043. }
  1044. @Override
  1045. protected void onCreate(Bundle savedInstanceState) {
  1046. super.onCreate(savedInstanceState);
  1047. if (savedInstanceState != null) {
  1048. mIsAccountChooserActive = savedInstanceState.getBoolean(KEY_IS_ACCOUNT_CHOOSER_ACTIVE, false);
  1049. mCheckedMenuItem = savedInstanceState.getInt(KEY_CHECKED_MENU_ITEM, Menu.NONE);
  1050. }
  1051. mCurrentAccountAvatarRadiusDimension = getResources()
  1052. .getDimension(R.dimen.nav_drawer_header_avatar_radius);
  1053. mOtherAccountAvatarRadiusDimension = getResources()
  1054. .getDimension(R.dimen.nav_drawer_header_avatar_other_accounts_radius);
  1055. mMenuAccountAvatarRadiusDimension = getResources()
  1056. .getDimension(R.dimen.nav_drawer_menu_avatar_radius);
  1057. externalLinksProvider = new ExternalLinksProvider(getContentResolver());
  1058. arbitraryDataProvider = new ArbitraryDataProvider(getContentResolver());
  1059. }
  1060. @Override
  1061. protected void onSaveInstanceState(@NonNull Bundle outState) {
  1062. super.onSaveInstanceState(outState);
  1063. outState.putBoolean(KEY_IS_ACCOUNT_CHOOSER_ACTIVE, mIsAccountChooserActive);
  1064. outState.putInt(KEY_CHECKED_MENU_ITEM, mCheckedMenuItem);
  1065. }
  1066. @Override
  1067. public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
  1068. super.onRestoreInstanceState(savedInstanceState);
  1069. mIsAccountChooserActive = savedInstanceState.getBoolean(KEY_IS_ACCOUNT_CHOOSER_ACTIVE, false);
  1070. mCheckedMenuItem = savedInstanceState.getInt(KEY_CHECKED_MENU_ITEM, Menu.NONE);
  1071. // (re-)setup drawer state
  1072. showMenu();
  1073. // check/highlight the menu item if present
  1074. if (mCheckedMenuItem > Menu.NONE || mCheckedMenuItem < Menu.NONE) {
  1075. setDrawerMenuItemChecked(mCheckedMenuItem);
  1076. }
  1077. }
  1078. @Override
  1079. protected void onPostCreate(Bundle savedInstanceState) {
  1080. super.onPostCreate(savedInstanceState);
  1081. // Sync the toggle state after onRestoreInstanceState has occurred.
  1082. if (mDrawerToggle != null) {
  1083. mDrawerToggle.syncState();
  1084. if (isDrawerOpen()) {
  1085. mDrawerToggle.setDrawerIndicatorEnabled(true);
  1086. }
  1087. }
  1088. updateAccountList();
  1089. updateExternalLinksInDrawer();
  1090. updateQuotaLink();
  1091. updateHeaderBackground();
  1092. }
  1093. @Override
  1094. public void onConfigurationChanged(@NonNull Configuration newConfig) {
  1095. super.onConfigurationChanged(newConfig);
  1096. if (mDrawerToggle != null) {
  1097. mDrawerToggle.onConfigurationChanged(newConfig);
  1098. }
  1099. }
  1100. @Override
  1101. public void onBackPressed() {
  1102. if (isDrawerOpen()) {
  1103. closeDrawer();
  1104. return;
  1105. }
  1106. super.onBackPressed();
  1107. }
  1108. @Override
  1109. protected void onResume() {
  1110. super.onResume();
  1111. if (AppCompatDelegate.getDefaultNightMode() != AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) {
  1112. getDelegate().setLocalNightMode(DarkMode.DARK == preferences.getDarkThemeMode() ?
  1113. AppCompatDelegate.MODE_NIGHT_YES : AppCompatDelegate.MODE_NIGHT_NO);
  1114. getDelegate().applyDayNight();
  1115. }
  1116. setDrawerMenuItemChecked(mCheckedMenuItem);
  1117. }
  1118. @Override
  1119. protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  1120. super.onActivityResult(requestCode, resultCode, data);
  1121. // update Account list and active account if Manage Account activity replies with
  1122. // - ACCOUNT_LIST_CHANGED = true
  1123. // - RESULT_OK
  1124. if (requestCode == ACTION_MANAGE_ACCOUNTS && resultCode == RESULT_OK
  1125. && data.getBooleanExtra(ManageAccountsActivity.KEY_ACCOUNT_LIST_CHANGED, false)) {
  1126. // current account has changed
  1127. if (data.getBooleanExtra(ManageAccountsActivity.KEY_CURRENT_ACCOUNT_CHANGED, false)) {
  1128. setAccount(accountManager.getCurrentAccount(), false);
  1129. updateAccountList();
  1130. restart();
  1131. } else {
  1132. updateAccountList();
  1133. }
  1134. } else if (requestCode == PassCodeManager.PASSCODE_ACTIVITY &&
  1135. Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && data != null) {
  1136. int result = data.getIntExtra(RequestCredentialsActivity.KEY_CHECK_RESULT,
  1137. RequestCredentialsActivity.KEY_CHECK_RESULT_FALSE);
  1138. if (result == RequestCredentialsActivity.KEY_CHECK_RESULT_CANCEL) {
  1139. Log_OC.d(TAG, "PassCodeManager cancelled");
  1140. preferences.setLockTimestamp(0);
  1141. finish();
  1142. }
  1143. }
  1144. }
  1145. /**
  1146. * Finds a view that was identified by the id attribute from the drawer header.
  1147. *
  1148. * @param id the view's id
  1149. * @return The view if found or <code>null</code> otherwise.
  1150. */
  1151. private View findNavigationViewChildById(int id) {
  1152. NavigationView view = findViewById(R.id.nav_view);
  1153. if (view != null) {
  1154. return view.getHeaderView(0).findViewById(id);
  1155. } else {
  1156. return null;
  1157. }
  1158. }
  1159. /**
  1160. * Quota view can be either at navigation bottom or header
  1161. *
  1162. * @param id the view's id
  1163. * @return The view if found or <code>null</code> otherwise.
  1164. */
  1165. private View findQuotaViewById(int id) {
  1166. View v = ((NavigationView) findViewById(R.id.nav_view)).getHeaderView(0).findViewById(id);
  1167. if (v != null) {
  1168. return v;
  1169. } else {
  1170. return findViewById(id);
  1171. }
  1172. }
  1173. /**
  1174. * restart helper method which is called after a changing the current account.
  1175. */
  1176. protected abstract void restart();
  1177. /**
  1178. * Get list of users suitable for displaying in navigation drawer header.
  1179. * First item is always current {@link User}. Remaining items are other
  1180. * users possible to switch to.
  1181. *
  1182. * @return List of available users
  1183. */
  1184. @NonNull
  1185. private List<User> getUserAvatars() {
  1186. User currentUser = accountManager.getUser();
  1187. List<User> availableUsers = CollectionsKt.filter(accountManager.getAllUsers(), user ->
  1188. !TextUtils.equals(user.getAccountName(), currentUser.getAccountName()) &&
  1189. !arbitraryDataProvider.getBooleanValue(user.toPlatformAccount(),
  1190. ManageAccountsActivity.PENDING_FOR_REMOVAL)
  1191. );
  1192. availableUsers.add(0, currentUser);
  1193. return availableUsers;
  1194. }
  1195. @Override
  1196. public void avatarGenerated(Drawable avatarDrawable, Object callContext) {
  1197. if (callContext instanceof MenuItem) {
  1198. MenuItem mi = (MenuItem) callContext;
  1199. mi.setIcon(avatarDrawable);
  1200. } else if (callContext instanceof ImageView) {
  1201. ImageView iv = (ImageView) callContext;
  1202. iv.setImageDrawable(avatarDrawable);
  1203. }
  1204. }
  1205. @Override
  1206. public boolean shouldCallGeneratedCallback(String tag, Object callContext) {
  1207. if (callContext instanceof MenuItem) {
  1208. MenuItem mi = (MenuItem) callContext;
  1209. return String.valueOf(mi.getTitle()).equals(tag);
  1210. } else if (callContext instanceof ImageView) {
  1211. ImageView iv = (ImageView) callContext;
  1212. return String.valueOf(iv.getTag()).equals(tag);
  1213. }
  1214. return false;
  1215. }
  1216. /**
  1217. * Adds other listeners to react on changes of the drawer layout.
  1218. *
  1219. * @param listener Object interested in changes of the drawer layout.
  1220. */
  1221. public void addDrawerListener(DrawerLayout.DrawerListener listener) {
  1222. if (mDrawerLayout != null) {
  1223. mDrawerLayout.addDrawerListener(listener);
  1224. } else {
  1225. Log_OC.e(TAG, "Drawer layout not ready to add drawer listener");
  1226. }
  1227. }
  1228. public boolean isDrawerIndicatorAvailable() {
  1229. return true;
  1230. }
  1231. @Override
  1232. protected void onStart() {
  1233. super.onStart();
  1234. EventBus.getDefault().register(this);
  1235. }
  1236. @Override
  1237. protected void onStop() {
  1238. if (preferences.getLockTimestamp() != 0) {
  1239. preferences.setLockTimestamp(SystemClock.elapsedRealtime());
  1240. }
  1241. EventBus.getDefault().unregister(this);
  1242. super.onStop();
  1243. }
  1244. @Subscribe(threadMode = ThreadMode.MAIN)
  1245. public void onAccountRemovedEvent(AccountRemovedEvent event) {
  1246. updateAccountList();
  1247. restart();
  1248. }
  1249. /**
  1250. * Retrieves external links via api from 'external' app
  1251. */
  1252. public void fetchExternalLinks(final boolean force) {
  1253. if (getBaseContext().getResources().getBoolean(R.bool.show_external_links)) {
  1254. Thread t = new Thread(() -> {
  1255. // fetch capabilities as early as possible
  1256. if ((getCapabilities() == null || getCapabilities().getAccountName().isEmpty())
  1257. && getStorageManager() != null) {
  1258. GetCapabilitiesOperation getCapabilities = new GetCapabilitiesOperation();
  1259. getCapabilities.execute(getStorageManager(), getBaseContext());
  1260. }
  1261. User user = accountManager.getUser();
  1262. String name = user.getAccountName();
  1263. if (getStorageManager() != null && getStorageManager().getCapability(name) != null &&
  1264. getStorageManager().getCapability(name).getExternalLinks().isTrue()) {
  1265. int count = arbitraryDataProvider.getIntegerValue(FilesSyncHelper.GLOBAL,
  1266. FileActivity.APP_OPENED_COUNT);
  1267. if (count > 10 || count == -1 || force) {
  1268. if (force) {
  1269. Log_OC.d("ExternalLinks", "force update");
  1270. }
  1271. arbitraryDataProvider.storeOrUpdateKeyValue(FilesSyncHelper.GLOBAL,
  1272. FileActivity.APP_OPENED_COUNT, "0");
  1273. Log_OC.d("ExternalLinks", "update via api");
  1274. RemoteOperation getExternalLinksOperation = new ExternalLinksOperation();
  1275. RemoteOperationResult result = getExternalLinksOperation.execute(user.toPlatformAccount(), this);
  1276. if (result.isSuccess() && result.getData() != null) {
  1277. externalLinksProvider.deleteAllExternalLinks();
  1278. ArrayList<ExternalLink> externalLinks = (ArrayList<ExternalLink>) (Object) result.getData();
  1279. for (ExternalLink link : externalLinks) {
  1280. externalLinksProvider.storeExternalLink(link);
  1281. }
  1282. }
  1283. } else {
  1284. arbitraryDataProvider.storeOrUpdateKeyValue(FilesSyncHelper.GLOBAL,
  1285. FileActivity.APP_OPENED_COUNT, String.valueOf(count + 1));
  1286. }
  1287. } else {
  1288. externalLinksProvider.deleteAllExternalLinks();
  1289. Log_OC.d("ExternalLinks", "links disabled");
  1290. }
  1291. runOnUiThread(this::updateExternalLinksInDrawer);
  1292. });
  1293. t.start();
  1294. }
  1295. }
  1296. }