DrawerActivity.java 61 KB

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