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. onNavigationItemClicked(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 onNavigationItemClicked(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. showFiles(false);
  358. EventBus.getDefault().post(new ChangeMenuEvent());
  359. }
  360. } else {
  361. showFiles(false);
  362. Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class);
  363. intent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItem.getItemId());
  364. intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
  365. startActivity(intent);
  366. }
  367. break;
  368. case R.id.nav_favorites:
  369. handleSearchEvents(new SearchEvent("", SearchRemoteOperation.SearchType.FAVORITE_SEARCH),
  370. menuItem.getItemId());
  371. break;
  372. case R.id.nav_photos:
  373. startPhotoSearch(menuItem);
  374. break;
  375. case R.id.nav_on_device:
  376. EventBus.getDefault().post(new ChangeMenuEvent());
  377. showFiles(true);
  378. break;
  379. case R.id.nav_uploads:
  380. startActivity(UploadListActivity.class, Intent.FLAG_ACTIVITY_CLEAR_TOP);
  381. break;
  382. case R.id.nav_trashbin:
  383. startActivity(TrashbinActivity.class, Intent.FLAG_ACTIVITY_CLEAR_TOP);
  384. break;
  385. case R.id.nav_activity:
  386. startActivity(ActivitiesActivity.class, Intent.FLAG_ACTIVITY_CLEAR_TOP);
  387. break;
  388. case R.id.nav_notifications:
  389. startActivity(NotificationsActivity.class);
  390. break;
  391. case R.id.nav_synced_folders:
  392. startActivity(SyncedFoldersActivity.class);
  393. break;
  394. case R.id.nav_contacts:
  395. startActivity(ContactsPreferenceActivity.class);
  396. break;
  397. case R.id.nav_settings:
  398. startActivity(SettingsActivity.class);
  399. break;
  400. case R.id.nav_community:
  401. startActivity(CommunityActivity.class);
  402. break;
  403. case R.id.nav_logout:
  404. mCheckedMenuItem = -1;
  405. menuItem.setChecked(false);
  406. final Optional<User> optionalUser = getUser();
  407. if (optionalUser.isPresent()) {
  408. UserInfoActivity.openAccountRemovalConfirmationDialog(optionalUser.get(), getSupportFragmentManager());
  409. }
  410. break;
  411. case R.id.nav_shared:
  412. handleSearchEvents(new SearchEvent("", SearchRemoteOperation.SearchType.SHARED_FILTER),
  413. menuItem.getItemId());
  414. break;
  415. case R.id.nav_recently_modified:
  416. handleSearchEvents(new SearchEvent("", SearchRemoteOperation.SearchType.RECENTLY_MODIFIED_SEARCH),
  417. menuItem.getItemId());
  418. break;
  419. default:
  420. if (menuItem.getItemId() >= MENU_ITEM_EXTERNAL_LINK &&
  421. menuItem.getItemId() <= MENU_ITEM_EXTERNAL_LINK + 100) {
  422. // external link clicked
  423. externalLinkClicked(menuItem);
  424. } else {
  425. Log_OC.i(TAG, "Unknown drawer menu item clicked: " + menuItem.getTitle());
  426. }
  427. break;
  428. }
  429. }
  430. private void startActivity(Class<? extends Activity> activity) {
  431. startActivity(new Intent(getApplicationContext(), activity));
  432. }
  433. private void startActivity(Class<? extends Activity> activity, int flags) {
  434. Intent intent = new Intent(getApplicationContext(), activity);
  435. intent.setFlags(flags);
  436. startActivity(intent);
  437. }
  438. private void handleAccountItemClick(MenuItem menuItem) {
  439. switch (menuItem.getItemId()) {
  440. case R.id.drawer_menu_account_add:
  441. boolean isProviderOrOwnInstallationVisible = getResources()
  442. .getBoolean(R.bool.show_provider_or_own_installation);
  443. if (isProviderOrOwnInstallationVisible) {
  444. Intent firstRunIntent = new Intent(getApplicationContext(), FirstRunActivity.class);
  445. firstRunIntent.putExtra(FirstRunActivity.EXTRA_ALLOW_CLOSE, true);
  446. startActivity(firstRunIntent);
  447. } else {
  448. startAccountCreation();
  449. }
  450. break;
  451. case R.id.drawer_menu_account_manage:
  452. Intent manageAccountsIntent = new Intent(getApplicationContext(), ManageAccountsActivity.class);
  453. startActivityForResult(manageAccountsIntent, ACTION_MANAGE_ACCOUNTS);
  454. break;
  455. default:
  456. accountClicked(menuItem.getItemId());
  457. break;
  458. }
  459. }
  460. private void startPhotoSearch(MenuItem menuItem) {
  461. SearchEvent searchEvent = new SearchEvent("image/%", SearchRemoteOperation.SearchType.PHOTO_SEARCH);
  462. Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class);
  463. intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
  464. intent.setAction(Intent.ACTION_SEARCH);
  465. intent.putExtra(OCFileListFragment.SEARCH_EVENT, Parcels.wrap(searchEvent));
  466. intent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItem.getItemId());
  467. startActivity(intent);
  468. }
  469. private void handleSearchEvents(SearchEvent searchEvent, int menuItemId) {
  470. if (this instanceof FileDisplayActivity) {
  471. if (((FileDisplayActivity) this).getListOfFilesFragment() instanceof PhotoFragment) {
  472. Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class);
  473. intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
  474. intent.setAction(Intent.ACTION_SEARCH);
  475. intent.putExtra(OCFileListFragment.SEARCH_EVENT, Parcels.wrap(searchEvent));
  476. intent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItemId);
  477. startActivity(intent);
  478. } else {
  479. EventBus.getDefault().post(searchEvent);
  480. }
  481. } else {
  482. Intent intent = new Intent(getApplicationContext(), FileDisplayActivity.class);
  483. intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
  484. intent.setAction(Intent.ACTION_SEARCH);
  485. intent.putExtra(OCFileListFragment.SEARCH_EVENT, Parcels.wrap(searchEvent));
  486. intent.putExtra(FileDisplayActivity.DRAWER_MENU_ID, menuItemId);
  487. startActivity(intent);
  488. }
  489. }
  490. /**
  491. * show the file list to the user.
  492. *
  493. * @param onDeviceOnly flag to decide if all files or only the ones on the device should be shown
  494. */
  495. public abstract void showFiles(boolean onDeviceOnly);
  496. /**
  497. * sets the new/current account and restarts. In case the given account equals the actual/current account the
  498. * call will be ignored.
  499. *
  500. * @param hashCode HashCode of account to be set
  501. */
  502. private void accountClicked(int hashCode) {
  503. final User currentUser = accountManager.getUser();
  504. if (currentUser.hashCode() != hashCode && accountManager.setCurrentOwnCloudAccount(hashCode)) {
  505. fetchExternalLinks(true);
  506. restart();
  507. }
  508. }
  509. private void externalLinkClicked(MenuItem menuItem){
  510. for (ExternalLink link : externalLinksProvider.getExternalLink(ExternalLinkType.LINK)) {
  511. if (menuItem.getTitle().toString().equalsIgnoreCase(link.name)) {
  512. if (link.redirect) {
  513. Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(link.url));
  514. DisplayUtils.startIntentIfAppAvailable(intent, this, R.string.no_browser_available);
  515. } else {
  516. Intent externalWebViewIntent = new Intent(getApplicationContext(), ExternalSiteWebView.class);
  517. externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_TITLE, link.name);
  518. externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_URL, link.url);
  519. externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, true);
  520. externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_MENU_ITEM_ID, menuItem.getItemId());
  521. startActivity(externalWebViewIntent);
  522. }
  523. }
  524. }
  525. }
  526. /**
  527. * click method for mini avatars in drawer header.
  528. *
  529. * @param view the clicked ImageView
  530. */
  531. public void onAccountDrawerClick(View view) {
  532. accountClicked((int) view.getTag());
  533. }
  534. /**
  535. * checks if the drawer exists and is opened.
  536. *
  537. * @return <code>true</code> if the drawer is open, else <code>false</code>
  538. */
  539. public boolean isDrawerOpen() {
  540. return mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START);
  541. }
  542. /**
  543. * closes the drawer.
  544. */
  545. public void closeDrawer() {
  546. if (mDrawerLayout != null) {
  547. mDrawerLayout.closeDrawer(GravityCompat.START);
  548. }
  549. }
  550. /**
  551. * opens the drawer.
  552. */
  553. public void openDrawer() {
  554. if (mDrawerLayout != null) {
  555. mDrawerLayout.openDrawer(GravityCompat.START);
  556. updateExternalLinksInDrawer();
  557. updateQuotaLink();
  558. }
  559. }
  560. /**
  561. * Enable or disable interaction with all drawers.
  562. *
  563. * @param lockMode The new lock mode for the given drawer. One of {@link DrawerLayout#LOCK_MODE_UNLOCKED},
  564. * {@link DrawerLayout#LOCK_MODE_LOCKED_CLOSED} or {@link DrawerLayout#LOCK_MODE_LOCKED_OPEN}.
  565. */
  566. public void setDrawerLockMode(int lockMode) {
  567. if (mDrawerLayout != null) {
  568. mDrawerLayout.setDrawerLockMode(lockMode);
  569. }
  570. }
  571. /**
  572. * Enable or disable the drawer indicator.
  573. *
  574. * @param enable true to enable, false to disable
  575. */
  576. public void setDrawerIndicatorEnabled(boolean enable) {
  577. if (mDrawerToggle != null) {
  578. mDrawerToggle.setDrawerIndicatorEnabled(enable);
  579. }
  580. }
  581. /**
  582. * updates the account list in the drawer.
  583. */
  584. public void updateAccountList() {
  585. List<User> users = accountManager.getAllUsers();
  586. ArrayList<User> persistingAccounts = new ArrayList<>();
  587. for (User user: users) {
  588. boolean pendingForRemoval = arbitraryDataProvider.getBooleanValue(user.toPlatformAccount(),
  589. ManageAccountsActivity.PENDING_FOR_REMOVAL);
  590. if (!pendingForRemoval) {
  591. persistingAccounts.add(user);
  592. }
  593. }
  594. if (mNavigationView != null && mDrawerLayout != null) {
  595. if (persistingAccounts.size() > 0) {
  596. repopulateAccountList(persistingAccounts);
  597. setAccountInDrawer(accountManager.getUser());
  598. mAvatars = getUserAvatars();
  599. // activate second/end account avatar
  600. final User secondUser = mAvatars.size() > 1 ? mAvatars.get(1) : null;
  601. if (secondUser != null) {
  602. mAccountEndAccountAvatar.setTag(secondUser.hashCode());
  603. DisplayUtils.setAvatar(secondUser,
  604. this,
  605. mOtherAccountAvatarRadiusDimension,
  606. getResources(),
  607. mAccountEndAccountAvatar,
  608. this);
  609. mAccountEndAccountAvatar.setVisibility(View.VISIBLE);
  610. } else {
  611. mAccountEndAccountAvatar.setVisibility(View.GONE);
  612. }
  613. // activate third/middle account avatar
  614. final User thirdUser = mAvatars.size() > 2 ? mAvatars.get(2) : null;
  615. if (thirdUser != null) {
  616. mAccountMiddleAccountAvatar.setTag(thirdUser.hashCode());
  617. DisplayUtils.setAvatar(thirdUser,
  618. this,
  619. mOtherAccountAvatarRadiusDimension,
  620. getResources(),
  621. mAccountMiddleAccountAvatar,
  622. this);
  623. mAccountMiddleAccountAvatar.setVisibility(View.VISIBLE);
  624. } else {
  625. mAccountMiddleAccountAvatar.setVisibility(View.GONE);
  626. }
  627. } else {
  628. mAccountEndAccountAvatar.setVisibility(View.GONE);
  629. mAccountMiddleAccountAvatar.setVisibility(View.GONE);
  630. }
  631. }
  632. }
  633. /**
  634. * re-populates the account list.
  635. *
  636. * @param users list of users
  637. */
  638. private void repopulateAccountList(List<User> users) {
  639. // remove all accounts from list
  640. mNavigationView.getMenu().removeGroup(R.id.drawer_menu_accounts);
  641. // add all accounts to list
  642. for (User user: users) {
  643. try {
  644. // show all accounts except the currently active one and those pending for removal
  645. if (!getAccount().name.equals(user.getAccountName())) {
  646. MenuItem accountMenuItem = mNavigationView.getMenu().add(
  647. R.id.drawer_menu_accounts,
  648. user.hashCode(),
  649. MENU_ORDER_ACCOUNT,
  650. DisplayUtils.getAccountNameDisplayText(user))
  651. .setIcon(TextDrawable.createAvatar(user.toPlatformAccount(),
  652. mMenuAccountAvatarRadiusDimension));
  653. DisplayUtils.setAvatar(user, this, mMenuAccountAvatarRadiusDimension, getResources(),
  654. accountMenuItem, this);
  655. }
  656. } catch (Exception e) {
  657. Log_OC.e(TAG, "Error calculating RGB value for account menu item.", e);
  658. mNavigationView.getMenu().add(
  659. R.id.drawer_menu_accounts,
  660. user.hashCode(),
  661. MENU_ORDER_ACCOUNT,
  662. DisplayUtils.getAccountNameDisplayText(user))
  663. .setIcon(R.drawable.ic_user);
  664. }
  665. }
  666. // re-add add-account and manage-accounts
  667. mNavigationView.getMenu().add(R.id.drawer_menu_accounts, R.id.drawer_menu_account_add,
  668. MENU_ORDER_ACCOUNT_FUNCTION,
  669. getResources().getString(R.string.prefs_add_account)).setIcon(R.drawable.ic_account_plus);
  670. mNavigationView.getMenu().add(R.id.drawer_menu_accounts, R.id.drawer_menu_account_manage,
  671. MENU_ORDER_ACCOUNT_FUNCTION,
  672. getResources().getString(R.string.drawer_manage_accounts)).setIcon(R.drawable.nav_settings);
  673. // adding sets menu group back to visible, so safety check and setting invisible
  674. showMenu();
  675. }
  676. /**
  677. * Updates title bar and home buttons (state and icon).
  678. * Assumes that navigation drawer is NOT visible.
  679. */
  680. protected void updateActionBarTitleAndHomeButton(OCFile chosenFile) {
  681. super.updateActionBarTitleAndHomeButton(chosenFile);
  682. // set home button properties
  683. if (mDrawerToggle != null && chosenFile != null) {
  684. if (isRoot(chosenFile)) {
  685. mDrawerToggle.setDrawerIndicatorEnabled(true);
  686. } else {
  687. mDrawerToggle.setDrawerIndicatorEnabled(false);
  688. Drawable upArrow = getResources().getDrawable(R.drawable.ic_arrow_back);
  689. upArrow.setColorFilter(ThemeUtils.fontColor(this), PorterDuff.Mode.SRC_ATOP);
  690. mDrawerToggle.setHomeAsUpIndicator(upArrow);
  691. }
  692. } else if (mDrawerToggle != null) {
  693. mDrawerToggle.setDrawerIndicatorEnabled(false);
  694. }
  695. }
  696. /**
  697. * sets the given account name in the drawer in case the drawer is available. The account name is shortened
  698. * beginning from the @-sign in the username.
  699. *
  700. * @param user the account to be set in the drawer
  701. */
  702. protected void setAccountInDrawer(User user) {
  703. if (mDrawerLayout != null && user != null) {
  704. TextView username = (TextView) findNavigationViewChildById(R.id.drawer_username);
  705. TextView usernameFull = (TextView) findNavigationViewChildById(R.id.drawer_username_full);
  706. String name = user.getAccountName();
  707. usernameFull.setText(DisplayUtils.convertIdn(name.substring(name.lastIndexOf('@') + 1),
  708. false));
  709. usernameFull.setTextColor(ThemeUtils.fontColor(this));
  710. username.setText(user.toOwnCloudAccount().getDisplayName());
  711. username.setTextColor(ThemeUtils.fontColor(this));
  712. View currentAccountView = findNavigationViewChildById(R.id.drawer_current_account);
  713. currentAccountView.setTag(name);
  714. DisplayUtils.setAvatar(user, this, mCurrentAccountAvatarRadiusDimension, getResources(),
  715. currentAccountView, this);
  716. // check and show quota info if available
  717. getAndDisplayUserQuota();
  718. }
  719. }
  720. /**
  721. * Toggle between standard menu and account list including saving the state.
  722. */
  723. private void toggleAccountList() {
  724. mIsAccountChooserActive = !mIsAccountChooserActive;
  725. showMenu();
  726. }
  727. /**
  728. * depending on the #mIsAccountChooserActive flag shows the account chooser or the standard menu.
  729. */
  730. private void showMenu() {
  731. if (mNavigationView != null) {
  732. if (mIsAccountChooserActive) {
  733. if (mAccountChooserToggle != null) {
  734. mAccountChooserToggle.setImageResource(R.drawable.ic_up);
  735. }
  736. mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, true);
  737. if (!getResources().getBoolean(R.bool.multiaccount_support) &&
  738. mNavigationView.getMenu().findItem(R.id.drawer_menu_account_add) != null) {
  739. mNavigationView.getMenu().removeItem(R.id.drawer_menu_account_add);
  740. }
  741. mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_standard, false);
  742. mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_external_links, false);
  743. mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_bottom, false);
  744. } else {
  745. if (mAccountChooserToggle != null) {
  746. mAccountChooserToggle.setImageResource(R.drawable.ic_down);
  747. }
  748. mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, false);
  749. mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_standard, true);
  750. mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_external_links, true);
  751. mNavigationView.getMenu().setGroupVisible(R.id.drawer_menu_bottom, true);
  752. }
  753. }
  754. }
  755. /**
  756. * shows or hides the quota UI elements.
  757. *
  758. * @param showQuota show/hide quota information
  759. */
  760. private void showQuota(boolean showQuota) {
  761. if (showQuota) {
  762. mQuotaView.setVisibility(View.VISIBLE);
  763. } else {
  764. mQuotaView.setVisibility(View.GONE);
  765. }
  766. }
  767. /**
  768. * configured the quota to be displayed.
  769. * @param usedSpace the used space
  770. * @param totalSpace the total space
  771. * @param relative the percentage of space already used
  772. * @param quotaValue {@link GetUserInfoRemoteOperation#SPACE_UNLIMITED} or other to determinate state
  773. */
  774. private void setQuotaInformation(long usedSpace, long totalSpace, int relative, long quotaValue) {
  775. if (GetUserInfoRemoteOperation.SPACE_UNLIMITED == quotaValue) {
  776. mQuotaTextPercentage.setText(String.format(
  777. getString(R.string.drawer_quota_unlimited),
  778. DisplayUtils.bytesToHumanReadable(usedSpace)));
  779. } else {
  780. mQuotaTextPercentage.setText(String.format(
  781. getString(R.string.drawer_quota),
  782. DisplayUtils.bytesToHumanReadable(usedSpace),
  783. DisplayUtils.bytesToHumanReadable(totalSpace)));
  784. }
  785. mQuotaProgressBar.setProgress(relative);
  786. ThemeUtils.colorProgressBar(mQuotaProgressBar, DisplayUtils.getRelativeInfoColor(this, relative));
  787. updateQuotaLink();
  788. showQuota(true);
  789. }
  790. protected void unsetAllDrawerMenuItems() {
  791. if (mNavigationView != null && mNavigationView.getMenu() != null) {
  792. Menu menu = mNavigationView.getMenu();
  793. for (int i = 0; i < menu.size(); i++) {
  794. menu.getItem(i).setChecked(false);
  795. }
  796. }
  797. mCheckedMenuItem = Menu.NONE;
  798. }
  799. private void updateQuotaLink() {
  800. if (mQuotaTextLink != null) {
  801. if (getBaseContext().getResources().getBoolean(R.bool.show_external_links)) {
  802. List<ExternalLink> quotas = externalLinksProvider.getExternalLink(ExternalLinkType.QUOTA);
  803. float density = getResources().getDisplayMetrics().density;
  804. final int size = Math.round(24 * density);
  805. if (quotas.size() > 0) {
  806. final ExternalLink firstQuota = quotas.get(0);
  807. mQuotaTextLink.setText(firstQuota.name);
  808. mQuotaTextLink.setClickable(true);
  809. mQuotaTextLink.setVisibility(View.VISIBLE);
  810. mQuotaTextLink.setOnClickListener(new View.OnClickListener() {
  811. @Override
  812. public void onClick(View v) {
  813. Intent externalWebViewIntent = new Intent(getApplicationContext(), ExternalSiteWebView.class);
  814. externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_TITLE, firstQuota.name);
  815. externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_URL, firstQuota.url);
  816. externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, true);
  817. externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_MENU_ITEM_ID, -1);
  818. startActivity(externalWebViewIntent);
  819. }
  820. });
  821. SimpleTarget target = new SimpleTarget<Drawable>() {
  822. @Override
  823. public void onResourceReady(Drawable resource, GlideAnimation glideAnimation) {
  824. Drawable test = resource.getCurrent();
  825. test.setBounds(0, 0, size, size);
  826. mQuotaTextLink.setCompoundDrawablesWithIntrinsicBounds(test, null, null, null);
  827. }
  828. @Override
  829. public void onLoadFailed(Exception e, Drawable errorDrawable) {
  830. super.onLoadFailed(e, errorDrawable);
  831. Drawable test = errorDrawable.getCurrent();
  832. test.setBounds(0, 0, size, size);
  833. mQuotaTextLink.setCompoundDrawablesWithIntrinsicBounds(test, null, null, null);
  834. }
  835. };
  836. DisplayUtils.downloadIcon(getUserAccountManager(),
  837. clientFactory,
  838. this,
  839. firstQuota.iconUrl,
  840. target,
  841. R.drawable.ic_link,
  842. size,
  843. size);
  844. } else {
  845. mQuotaTextLink.setVisibility(View.GONE);
  846. }
  847. } else {
  848. mQuotaTextLink.setVisibility(View.GONE);
  849. }
  850. }
  851. }
  852. /**
  853. * checks/highlights the provided menu item if the drawer has been initialized and the menu item exists.
  854. *
  855. * @param menuItemId the menu item to be highlighted
  856. */
  857. protected void setDrawerMenuItemChecked(int menuItemId) {
  858. if (mNavigationView != null && mNavigationView.getMenu() != null &&
  859. mNavigationView.getMenu().findItem(menuItemId) != null) {
  860. MenuItem item = mNavigationView.getMenu().findItem(menuItemId);
  861. item.setChecked(true);
  862. // reset all tinted icons
  863. for (int i = 0; i < mNavigationView.getMenu().size(); i++) {
  864. MenuItem menuItem = mNavigationView.getMenu().getItem(i);
  865. if (menuItem.getIcon() != null) {
  866. menuItem.getIcon().clearColorFilter();
  867. if (menuItem.getGroupId() != R.id.drawer_menu_accounts
  868. || menuItem.getItemId() == R.id.drawer_menu_account_add
  869. || menuItem.getItemId() == R.id.drawer_menu_account_manage) {
  870. ThemeUtils.tintDrawable(
  871. menuItem.getIcon(), ContextCompat.getColor(this, R.color.drawer_menu_icon));
  872. }
  873. menuItem.setTitle(Html.fromHtml(
  874. "<font color='"
  875. + ThemeUtils.colorToHexString(ContextCompat.getColor(this, R.color.text_color))
  876. + "'>" + menuItem.getTitle()
  877. + "</font>"));
  878. }
  879. }
  880. int elementColor = ThemeUtils.elementColor(this);
  881. ThemeUtils.tintDrawable(item.getIcon(), elementColor);
  882. String colorHex = ThemeUtils.colorToHexString(elementColor);
  883. item.setTitle(Html.fromHtml("<font color='" + colorHex + "'>" + item.getTitle() + "</font>"));
  884. mCheckedMenuItem = menuItemId;
  885. } else {
  886. Log_OC.w(TAG, "setDrawerMenuItemChecked has been called with invalid menu-item-ID");
  887. }
  888. }
  889. /**
  890. * Retrieves and shows the user quota if available
  891. */
  892. private void getAndDisplayUserQuota() {
  893. // set user space information
  894. Thread t = new Thread(new Runnable() {
  895. public void run() {
  896. final User user = accountManager.getUser();
  897. if (user.isAnonymous()) {
  898. return;
  899. }
  900. final Context context = MainApp.getAppContext();
  901. RemoteOperationResult result = new GetUserInfoRemoteOperation().execute(user.toPlatformAccount(), context);
  902. if (result.isSuccess() && result.getData() != null) {
  903. final UserInfo userInfo = (UserInfo) result.getData().get(0);
  904. final Quota quota = userInfo.getQuota();
  905. if (quota != null) {
  906. final long used = quota.getUsed();
  907. final long total = quota.getTotal();
  908. final int relative = (int) Math.ceil(quota.getRelative());
  909. final long quotaValue = quota.getQuota();
  910. runOnUiThread(new Runnable() {
  911. @Override
  912. public void run() {
  913. if (quotaValue > 0 || quotaValue == GetUserInfoRemoteOperation.SPACE_UNLIMITED
  914. || quotaValue == GetUserInfoRemoteOperation.QUOTA_LIMIT_INFO_NOT_AVAILABLE) {
  915. /*
  916. * show quota in case
  917. * it is available and calculated (> 0) or
  918. * in case of legacy servers (==QUOTA_LIMIT_INFO_NOT_AVAILABLE)
  919. */
  920. setQuotaInformation(used, total, relative, quotaValue);
  921. } else {
  922. /*
  923. * quotaValue < 0 means special cases like
  924. * {@link RemoteGetUserQuotaOperation.SPACE_NOT_COMPUTED},
  925. * {@link RemoteGetUserQuotaOperation.SPACE_UNKNOWN} or
  926. * {@link RemoteGetUserQuotaOperation.SPACE_UNLIMITED}
  927. * thus don't display any quota information.
  928. */
  929. showQuota(false);
  930. }
  931. }
  932. });
  933. }
  934. }
  935. }
  936. });
  937. t.start();
  938. }
  939. public void updateExternalLinksInDrawer() {
  940. if (mNavigationView != null && getBaseContext().getResources().getBoolean(R.bool.show_external_links)) {
  941. mNavigationView.getMenu().removeGroup(R.id.drawer_menu_external_links);
  942. float density = getResources().getDisplayMetrics().density;
  943. final int size = Math.round(24 * density);
  944. int greyColor = ContextCompat.getColor(this, R.color.drawer_menu_icon);
  945. for (final ExternalLink link : externalLinksProvider.getExternalLink(ExternalLinkType.LINK)) {
  946. int id = mNavigationView.getMenu().add(R.id.drawer_menu_external_links,
  947. MENU_ITEM_EXTERNAL_LINK + link.id, MENU_ORDER_EXTERNAL_LINKS, link.name)
  948. .setCheckable(true).getItemId();
  949. MenuSimpleTarget target = new MenuSimpleTarget<Drawable>(id) {
  950. @Override
  951. public void onResourceReady(Drawable resource, GlideAnimation glideAnimation) {
  952. setExternalLinkIcon(getIdMenuItem(), resource, greyColor);
  953. }
  954. @Override
  955. public void onLoadFailed(Exception e, Drawable errorDrawable) {
  956. super.onLoadFailed(e, errorDrawable);
  957. setExternalLinkIcon(getIdMenuItem(), errorDrawable, greyColor);
  958. }
  959. };
  960. DisplayUtils.downloadIcon(getUserAccountManager(),
  961. clientFactory,
  962. this,
  963. link.iconUrl,
  964. target,
  965. R.drawable.ic_link,
  966. size,
  967. size);
  968. }
  969. setDrawerMenuItemChecked(mCheckedMenuItem);
  970. }
  971. }
  972. private void setExternalLinkIcon(int id, Drawable drawable, int greyColor) {
  973. MenuItem menuItem = mNavigationView.getMenu().findItem(id);
  974. if (menuItem != null) {
  975. if (drawable != null) {
  976. menuItem.setIcon(ThemeUtils.tintDrawable(drawable, greyColor));
  977. } else {
  978. menuItem.setIcon(R.drawable.ic_link);
  979. }
  980. }
  981. }
  982. public void updateHeaderBackground() {
  983. if (getAccount() != null &&
  984. getStorageManager().getCapability(getAccount().name).getServerBackground() != null) {
  985. final ViewGroup navigationHeader = (ViewGroup) findNavigationViewChildById(R.id.drawer_header_view);
  986. if (navigationHeader != null) {
  987. OCCapability capability = getStorageManager().getCapability(getAccount().name);
  988. String background = capability.getServerBackground();
  989. CapabilityBooleanType backgroundDefault = capability.getServerBackgroundDefault();
  990. CapabilityBooleanType backgroundPlain = capability.getServerBackgroundPlain();
  991. int primaryColor = ThemeUtils.primaryColor(getAccount(), false, this);
  992. if (backgroundDefault.isTrue() && backgroundPlain.isTrue()) {
  993. // use only solid color
  994. setNavigationHeaderBackground(new ColorDrawable(primaryColor), navigationHeader);
  995. } else if (backgroundDefault.isTrue() && backgroundPlain.isFalse()) {
  996. // use nc13 background image with themed color
  997. Drawable[] drawables = {new ColorDrawable(primaryColor),
  998. getResources().getDrawable(R.drawable.background)};
  999. LayerDrawable layerDrawable = new LayerDrawable(drawables);
  1000. setNavigationHeaderBackground(layerDrawable, navigationHeader);
  1001. } else {
  1002. // use url
  1003. if (URLUtil.isValidUrl(background) || background.isEmpty()) {
  1004. // background image
  1005. SimpleTarget target = new SimpleTarget<Drawable>() {
  1006. @Override
  1007. public void onResourceReady(Drawable resource, GlideAnimation glideAnimation) {
  1008. Drawable[] drawables = {new ColorDrawable(primaryColor), resource};
  1009. LayerDrawable layerDrawable = new LayerDrawable(drawables);
  1010. setNavigationHeaderBackground(layerDrawable, navigationHeader);
  1011. }
  1012. @Override
  1013. public void onLoadFailed(Exception e, Drawable errorDrawable) {
  1014. Drawable[] drawables = {new ColorDrawable(primaryColor), errorDrawable};
  1015. LayerDrawable layerDrawable = new LayerDrawable(drawables);
  1016. setNavigationHeaderBackground(layerDrawable, navigationHeader);
  1017. }
  1018. };
  1019. int backgroundResource;
  1020. OwnCloudVersion ownCloudVersion = accountManager.getServerVersion(getAccount());
  1021. if (ownCloudVersion.compareTo(OwnCloudVersion.nextcloud_18) >= 0) {
  1022. backgroundResource = R.drawable.background_nc18;
  1023. } else {
  1024. backgroundResource = R.drawable.background;
  1025. }
  1026. Glide.with(this)
  1027. .load(background)
  1028. .centerCrop()
  1029. .placeholder(backgroundResource)
  1030. .error(backgroundResource)
  1031. .crossFade()
  1032. .into(target);
  1033. } else {
  1034. // plain color
  1035. setNavigationHeaderBackground(new ColorDrawable(primaryColor), navigationHeader);
  1036. }
  1037. }
  1038. }
  1039. }
  1040. }
  1041. private void setNavigationHeaderBackground(Drawable drawable, ViewGroup navigationHeader) {
  1042. final ImageView background = navigationHeader.findViewById(R.id.drawer_header_background);
  1043. background.setImageDrawable(drawable);
  1044. }
  1045. @Override
  1046. protected void onCreate(Bundle savedInstanceState) {
  1047. super.onCreate(savedInstanceState);
  1048. if (savedInstanceState != null) {
  1049. mIsAccountChooserActive = savedInstanceState.getBoolean(KEY_IS_ACCOUNT_CHOOSER_ACTIVE, false);
  1050. mCheckedMenuItem = savedInstanceState.getInt(KEY_CHECKED_MENU_ITEM, Menu.NONE);
  1051. }
  1052. mCurrentAccountAvatarRadiusDimension = getResources()
  1053. .getDimension(R.dimen.nav_drawer_header_avatar_radius);
  1054. mOtherAccountAvatarRadiusDimension = getResources()
  1055. .getDimension(R.dimen.nav_drawer_header_avatar_other_accounts_radius);
  1056. mMenuAccountAvatarRadiusDimension = getResources()
  1057. .getDimension(R.dimen.nav_drawer_menu_avatar_radius);
  1058. externalLinksProvider = new ExternalLinksProvider(getContentResolver());
  1059. arbitraryDataProvider = new ArbitraryDataProvider(getContentResolver());
  1060. }
  1061. @Override
  1062. protected void onSaveInstanceState(@NonNull Bundle outState) {
  1063. super.onSaveInstanceState(outState);
  1064. outState.putBoolean(KEY_IS_ACCOUNT_CHOOSER_ACTIVE, mIsAccountChooserActive);
  1065. outState.putInt(KEY_CHECKED_MENU_ITEM, mCheckedMenuItem);
  1066. }
  1067. @Override
  1068. public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
  1069. super.onRestoreInstanceState(savedInstanceState);
  1070. mIsAccountChooserActive = savedInstanceState.getBoolean(KEY_IS_ACCOUNT_CHOOSER_ACTIVE, false);
  1071. mCheckedMenuItem = savedInstanceState.getInt(KEY_CHECKED_MENU_ITEM, Menu.NONE);
  1072. // (re-)setup drawer state
  1073. showMenu();
  1074. // check/highlight the menu item if present
  1075. if (mCheckedMenuItem > Menu.NONE || mCheckedMenuItem < Menu.NONE) {
  1076. setDrawerMenuItemChecked(mCheckedMenuItem);
  1077. }
  1078. }
  1079. @Override
  1080. protected void onPostCreate(Bundle savedInstanceState) {
  1081. super.onPostCreate(savedInstanceState);
  1082. // Sync the toggle state after onRestoreInstanceState has occurred.
  1083. if (mDrawerToggle != null) {
  1084. mDrawerToggle.syncState();
  1085. if (isDrawerOpen()) {
  1086. mDrawerToggle.setDrawerIndicatorEnabled(true);
  1087. }
  1088. }
  1089. updateAccountList();
  1090. updateExternalLinksInDrawer();
  1091. updateQuotaLink();
  1092. updateHeaderBackground();
  1093. }
  1094. @Override
  1095. public void onConfigurationChanged(@NonNull Configuration newConfig) {
  1096. super.onConfigurationChanged(newConfig);
  1097. if (mDrawerToggle != null) {
  1098. mDrawerToggle.onConfigurationChanged(newConfig);
  1099. }
  1100. }
  1101. @Override
  1102. public void onBackPressed() {
  1103. if (isDrawerOpen()) {
  1104. closeDrawer();
  1105. return;
  1106. }
  1107. super.onBackPressed();
  1108. }
  1109. @Override
  1110. protected void onResume() {
  1111. super.onResume();
  1112. if (AppCompatDelegate.getDefaultNightMode() != AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) {
  1113. getDelegate().setLocalNightMode(DarkMode.DARK == preferences.getDarkThemeMode() ?
  1114. AppCompatDelegate.MODE_NIGHT_YES : AppCompatDelegate.MODE_NIGHT_NO);
  1115. getDelegate().applyDayNight();
  1116. }
  1117. setDrawerMenuItemChecked(mCheckedMenuItem);
  1118. }
  1119. @Override
  1120. protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  1121. super.onActivityResult(requestCode, resultCode, data);
  1122. // update Account list and active account if Manage Account activity replies with
  1123. // - ACCOUNT_LIST_CHANGED = true
  1124. // - RESULT_OK
  1125. if (requestCode == ACTION_MANAGE_ACCOUNTS && resultCode == RESULT_OK
  1126. && data.getBooleanExtra(ManageAccountsActivity.KEY_ACCOUNT_LIST_CHANGED, false)) {
  1127. // current account has changed
  1128. if (data.getBooleanExtra(ManageAccountsActivity.KEY_CURRENT_ACCOUNT_CHANGED, false)) {
  1129. setAccount(accountManager.getCurrentAccount(), false);
  1130. updateAccountList();
  1131. restart();
  1132. } else {
  1133. updateAccountList();
  1134. }
  1135. } else if (requestCode == PassCodeManager.PASSCODE_ACTIVITY &&
  1136. Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && data != null) {
  1137. int result = data.getIntExtra(RequestCredentialsActivity.KEY_CHECK_RESULT,
  1138. RequestCredentialsActivity.KEY_CHECK_RESULT_FALSE);
  1139. if (result == RequestCredentialsActivity.KEY_CHECK_RESULT_CANCEL) {
  1140. Log_OC.d(TAG, "PassCodeManager cancelled");
  1141. preferences.setLockTimestamp(0);
  1142. finish();
  1143. }
  1144. }
  1145. }
  1146. /**
  1147. * Finds a view that was identified by the id attribute from the drawer header.
  1148. *
  1149. * @param id the view's id
  1150. * @return The view if found or <code>null</code> otherwise.
  1151. */
  1152. private View findNavigationViewChildById(int id) {
  1153. NavigationView view = findViewById(R.id.nav_view);
  1154. if (view != null) {
  1155. return view.getHeaderView(0).findViewById(id);
  1156. } else {
  1157. return null;
  1158. }
  1159. }
  1160. /**
  1161. * Quota view can be either at navigation bottom or header
  1162. *
  1163. * @param id the view's id
  1164. * @return The view if found or <code>null</code> otherwise.
  1165. */
  1166. private View findQuotaViewById(int id) {
  1167. View v = ((NavigationView) findViewById(R.id.nav_view)).getHeaderView(0).findViewById(id);
  1168. if (v != null) {
  1169. return v;
  1170. } else {
  1171. return findViewById(id);
  1172. }
  1173. }
  1174. /**
  1175. * restart helper method which is called after a changing the current account.
  1176. */
  1177. protected abstract void restart();
  1178. /**
  1179. * Get list of users suitable for displaying in navigation drawer header.
  1180. * First item is always current {@link User}. Remaining items are other
  1181. * users possible to switch to.
  1182. *
  1183. * @return List of available users
  1184. */
  1185. @NonNull
  1186. private List<User> getUserAvatars() {
  1187. User currentUser = accountManager.getUser();
  1188. List<User> availableUsers = CollectionsKt.filter(accountManager.getAllUsers(), user ->
  1189. !TextUtils.equals(user.getAccountName(), currentUser.getAccountName()) &&
  1190. !arbitraryDataProvider.getBooleanValue(user.toPlatformAccount(),
  1191. ManageAccountsActivity.PENDING_FOR_REMOVAL)
  1192. );
  1193. availableUsers.add(0, currentUser);
  1194. return availableUsers;
  1195. }
  1196. @Override
  1197. public void avatarGenerated(Drawable avatarDrawable, Object callContext) {
  1198. if (callContext instanceof MenuItem) {
  1199. MenuItem mi = (MenuItem) callContext;
  1200. mi.setIcon(avatarDrawable);
  1201. } else if (callContext instanceof ImageView) {
  1202. ImageView iv = (ImageView) callContext;
  1203. iv.setImageDrawable(avatarDrawable);
  1204. }
  1205. }
  1206. @Override
  1207. public boolean shouldCallGeneratedCallback(String tag, Object callContext) {
  1208. if (callContext instanceof MenuItem) {
  1209. MenuItem mi = (MenuItem) callContext;
  1210. return String.valueOf(mi.getTitle()).equals(tag);
  1211. } else if (callContext instanceof ImageView) {
  1212. ImageView iv = (ImageView) callContext;
  1213. return String.valueOf(iv.getTag()).equals(tag);
  1214. }
  1215. return false;
  1216. }
  1217. /**
  1218. * Adds other listeners to react on changes of the drawer layout.
  1219. *
  1220. * @param listener Object interested in changes of the drawer layout.
  1221. */
  1222. public void addDrawerListener(DrawerLayout.DrawerListener listener) {
  1223. if (mDrawerLayout != null) {
  1224. mDrawerLayout.addDrawerListener(listener);
  1225. } else {
  1226. Log_OC.e(TAG, "Drawer layout not ready to add drawer listener");
  1227. }
  1228. }
  1229. public boolean isDrawerIndicatorAvailable() {
  1230. return true;
  1231. }
  1232. @Override
  1233. protected void onStart() {
  1234. super.onStart();
  1235. EventBus.getDefault().register(this);
  1236. }
  1237. @Override
  1238. protected void onStop() {
  1239. if (preferences.getLockTimestamp() != 0) {
  1240. preferences.setLockTimestamp(SystemClock.elapsedRealtime());
  1241. }
  1242. EventBus.getDefault().unregister(this);
  1243. super.onStop();
  1244. }
  1245. @Subscribe(threadMode = ThreadMode.MAIN)
  1246. public void onAccountRemovedEvent(AccountRemovedEvent event) {
  1247. updateAccountList();
  1248. restart();
  1249. }
  1250. /**
  1251. * Retrieves external links via api from 'external' app
  1252. */
  1253. public void fetchExternalLinks(final boolean force) {
  1254. if (getBaseContext().getResources().getBoolean(R.bool.show_external_links)) {
  1255. Thread t = new Thread(() -> {
  1256. // fetch capabilities as early as possible
  1257. if ((getCapabilities() == null || getCapabilities().getAccountName().isEmpty())
  1258. && getStorageManager() != null) {
  1259. GetCapabilitiesOperation getCapabilities = new GetCapabilitiesOperation();
  1260. getCapabilities.execute(getStorageManager(), getBaseContext());
  1261. }
  1262. User user = accountManager.getUser();
  1263. String name = user.getAccountName();
  1264. if (getStorageManager() != null && getStorageManager().getCapability(name) != null &&
  1265. getStorageManager().getCapability(name).getExternalLinks().isTrue()) {
  1266. int count = arbitraryDataProvider.getIntegerValue(FilesSyncHelper.GLOBAL,
  1267. FileActivity.APP_OPENED_COUNT);
  1268. if (count > 10 || count == -1 || force) {
  1269. if (force) {
  1270. Log_OC.d("ExternalLinks", "force update");
  1271. }
  1272. arbitraryDataProvider.storeOrUpdateKeyValue(FilesSyncHelper.GLOBAL,
  1273. FileActivity.APP_OPENED_COUNT, "0");
  1274. Log_OC.d("ExternalLinks", "update via api");
  1275. RemoteOperation getExternalLinksOperation = new ExternalLinksOperation();
  1276. RemoteOperationResult result = getExternalLinksOperation.execute(user.toPlatformAccount(), this);
  1277. if (result.isSuccess() && result.getData() != null) {
  1278. externalLinksProvider.deleteAllExternalLinks();
  1279. ArrayList<ExternalLink> externalLinks = (ArrayList<ExternalLink>) (Object) result.getData();
  1280. for (ExternalLink link : externalLinks) {
  1281. externalLinksProvider.storeExternalLink(link);
  1282. }
  1283. }
  1284. } else {
  1285. arbitraryDataProvider.storeOrUpdateKeyValue(FilesSyncHelper.GLOBAL,
  1286. FileActivity.APP_OPENED_COUNT, String.valueOf(count + 1));
  1287. }
  1288. } else {
  1289. externalLinksProvider.deleteAllExternalLinks();
  1290. Log_OC.d("ExternalLinks", "links disabled");
  1291. }
  1292. runOnUiThread(this::updateExternalLinksInDrawer);
  1293. });
  1294. t.start();
  1295. }
  1296. }
  1297. }