Preferences.java 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002
  1. /*
  2. * ownCloud Android client application
  3. *
  4. * @author Bartek Przybylski
  5. * @author David A. Velasco
  6. * Copyright (C) 2011 Bartek Przybylski
  7. * Copyright (C) 2016 ownCloud Inc.
  8. * Copyright (C) 2016 Nextcloud
  9. *
  10. * This program is free software: you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License version 2,
  12. * as published by the Free Software Foundation.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. *
  22. */
  23. package com.owncloud.android.ui.activity;
  24. import android.accounts.Account;
  25. import android.app.Activity;
  26. import android.content.Intent;
  27. import android.content.SharedPreferences;
  28. import android.content.pm.PackageInfo;
  29. import android.content.pm.PackageManager.NameNotFoundException;
  30. import android.content.res.Configuration;
  31. import android.graphics.drawable.ColorDrawable;
  32. import android.graphics.drawable.Drawable;
  33. import android.net.Uri;
  34. import android.os.Build;
  35. import android.os.Bundle;
  36. import android.preference.ListPreference;
  37. import android.preference.Preference;
  38. import android.preference.PreferenceActivity;
  39. import android.preference.PreferenceCategory;
  40. import android.preference.PreferenceManager;
  41. import android.preference.PreferenceScreen;
  42. import android.preference.SwitchPreference;
  43. import android.view.Menu;
  44. import android.view.MenuInflater;
  45. import android.view.MenuItem;
  46. import android.view.View;
  47. import android.view.ViewGroup;
  48. import android.view.Window;
  49. import android.webkit.URLUtil;
  50. import com.owncloud.android.BuildConfig;
  51. import com.owncloud.android.MainApp;
  52. import com.owncloud.android.R;
  53. import com.owncloud.android.authentication.AccountUtils;
  54. import com.owncloud.android.authentication.PassCodeManager;
  55. import com.owncloud.android.datamodel.ArbitraryDataProvider;
  56. import com.owncloud.android.datamodel.ExternalLinksProvider;
  57. import com.owncloud.android.datastorage.DataStorageProvider;
  58. import com.owncloud.android.datastorage.StoragePoint;
  59. import com.owncloud.android.lib.common.ExternalLink;
  60. import com.owncloud.android.lib.common.ExternalLinkType;
  61. import com.owncloud.android.lib.common.OwnCloudAccount;
  62. import com.owncloud.android.lib.common.OwnCloudClientManagerFactory;
  63. import com.owncloud.android.lib.common.utils.Log_OC;
  64. import com.owncloud.android.ui.asynctasks.LoadingVersionNumberTask;
  65. import com.owncloud.android.utils.DeviceCredentialUtils;
  66. import com.owncloud.android.utils.DisplayUtils;
  67. import com.owncloud.android.utils.EncryptionUtils;
  68. import com.owncloud.android.utils.MimeTypeUtil;
  69. import com.owncloud.android.utils.ThemeUtils;
  70. import java.util.ArrayList;
  71. import androidx.annotation.LayoutRes;
  72. import androidx.annotation.NonNull;
  73. import androidx.appcompat.app.ActionBar;
  74. import androidx.appcompat.app.AlertDialog;
  75. import androidx.appcompat.app.AppCompatDelegate;
  76. import androidx.core.content.res.ResourcesCompat;
  77. /**
  78. * An Activity that allows the user to change the application's settings.
  79. *
  80. * It proxies the necessary calls via {@link androidx.appcompat.app.AppCompatDelegate} to be used with AppCompat.
  81. */
  82. public class Preferences extends PreferenceActivity
  83. implements StorageMigration.StorageMigrationProgressListener, LoadingVersionNumberTask.VersionDevInterface {
  84. private static final String TAG = Preferences.class.getSimpleName();
  85. public static final String PREFERENCE_LOCK= "lock";
  86. public static final String LOCK_NONE = "none";
  87. public static final String LOCK_PASSCODE = "passcode";
  88. public static final String LOCK_DEVICE_CREDENTIALS = "device_credentials";
  89. public final static String PREFERENCE_USE_FINGERPRINT = "use_fingerprint";
  90. private static final int ACTION_REQUEST_PASSCODE = 5;
  91. private static final int ACTION_CONFIRM_PASSCODE = 6;
  92. private static final int ACTION_CONFIRM_DEVICE_CREDENTIALS = 7;
  93. private static final int ACTION_REQUEST_CODE_DAVDROID_SETUP = 10;
  94. private static final String DAV_PATH = "/remote.php/dav";
  95. public static final String SYNCED_FOLDER_LIGHT_UPLOAD_ON_WIFI = "SYNCED_FOLDER_LIGHT_UPLOAD_ON_WIFI";
  96. /**
  97. * The user's server base uri.
  98. */
  99. private Uri mUri;
  100. private ListPreference mLock;
  101. private SwitchPreference mShowHiddenFiles;
  102. private AppCompatDelegate mDelegate;
  103. private ListPreference mPrefStoragePath;
  104. private String mStoragePath;
  105. private String pendingLock;
  106. private Account mAccount;
  107. private ArbitraryDataProvider mArbitraryDataProvider;
  108. public static class PreferenceKeys {
  109. public static final String STORAGE_PATH = "storage_path";
  110. }
  111. @SuppressWarnings("deprecation")
  112. @Override
  113. public void onCreate(Bundle savedInstanceState) {
  114. if (ThemeUtils.themingEnabled(this)) {
  115. setTheme(R.style.FallbackThemingTheme);
  116. }
  117. getDelegate().installViewFactory();
  118. getDelegate().onCreate(savedInstanceState);
  119. super.onCreate(savedInstanceState);
  120. addPreferencesFromResource(R.xml.preferences);
  121. setupActionBar();
  122. // Register context menu for list of preferences.
  123. registerForContextMenu(getListView());
  124. int accentColor = ThemeUtils.primaryAccentColor(this);
  125. String appVersion = getAppVersion();
  126. PreferenceScreen preferenceScreen = (PreferenceScreen) findPreference("preference_screen");
  127. mAccount = AccountUtils.getCurrentOwnCloudAccount(getApplicationContext());
  128. mArbitraryDataProvider = new ArbitraryDataProvider(getContentResolver());
  129. // retrieve user's base uri
  130. setupBaseUri();
  131. // General
  132. setupGeneralCategory(accentColor);
  133. // Synced folders
  134. setupAutoUploadCategory(accentColor, preferenceScreen);
  135. // Details
  136. setupDetailsCategory(accentColor, preferenceScreen);
  137. // More
  138. setupMoreCategory(accentColor, appVersion);
  139. // About
  140. setupAboutCategory(accentColor, appVersion);
  141. // Dev
  142. setupDevCategory(accentColor, preferenceScreen);
  143. }
  144. private void setupDevCategory(int accentColor, PreferenceScreen preferenceScreen) {
  145. // Dev category
  146. PreferenceCategory preferenceCategoryDev = (PreferenceCategory) findPreference("dev_category");
  147. if (getResources().getBoolean(R.bool.is_beta)) {
  148. preferenceCategoryDev.setTitle(ThemeUtils.getColoredTitle(getString(R.string.prefs_category_dev),
  149. accentColor));
  150. /* Link to dev apks */
  151. Preference pDevLink = findPreference("dev_link");
  152. if (pDevLink != null) {
  153. if (getResources().getBoolean(R.bool.dev_version_direct_download_enabled)) {
  154. pDevLink.setOnPreferenceClickListener(preference -> {
  155. FileActivity.checkForNewDevVersion(this, getApplicationContext());
  156. return true;
  157. });
  158. } else {
  159. preferenceCategoryDev.removePreference(pDevLink);
  160. }
  161. }
  162. /* Link to dev changelog */
  163. Preference pChangelogLink = findPreference("changelog_link");
  164. if (pChangelogLink != null) {
  165. pChangelogLink.setOnPreferenceClickListener(preference -> {
  166. DisplayUtils.startLinkIntent(this, R.string.dev_changelog);
  167. return true;
  168. });
  169. }
  170. } else {
  171. preferenceScreen.removePreference(preferenceCategoryDev);
  172. }
  173. }
  174. private void setupAboutCategory(int accentColor, String appVersion) {
  175. PreferenceCategory preferenceCategoryAbout = (PreferenceCategory) findPreference("about");
  176. preferenceCategoryAbout.setTitle(ThemeUtils.getColoredTitle(getString(R.string.prefs_category_about),
  177. accentColor));
  178. /* About App */
  179. Preference pAboutApp = findPreference("about_app");
  180. if (pAboutApp != null) {
  181. pAboutApp.setTitle(String.format(getString(R.string.about_android), getString(R.string.app_name)));
  182. pAboutApp.setSummary(String.format(getString(R.string.about_version), appVersion));
  183. }
  184. // license
  185. boolean licenseEnabled = getResources().getBoolean(R.bool.license_enabled);
  186. Preference licensePreference = findPreference("license");
  187. if (licensePreference != null) {
  188. if (licenseEnabled) {
  189. licensePreference.setSummary(R.string.prefs_gpl_v2);
  190. licensePreference.setOnPreferenceClickListener(preference -> {
  191. DisplayUtils.startLinkIntent(this, R.string.license_url);
  192. return true;
  193. });
  194. } else {
  195. preferenceCategoryAbout.removePreference(licensePreference);
  196. }
  197. }
  198. // privacy
  199. boolean privacyEnabled = getResources().getBoolean(R.bool.privacy_enabled);
  200. Preference privacyPreference = findPreference("privacy");
  201. if (privacyPreference != null) {
  202. if (privacyEnabled && URLUtil.isValidUrl(getString(R.string.privacy_url))) {
  203. privacyPreference.setOnPreferenceClickListener(preference -> {
  204. try {
  205. Uri privacyUrl = Uri.parse(getString(R.string.privacy_url));
  206. String mimeType = MimeTypeUtil.getBestMimeTypeByFilename(privacyUrl.getLastPathSegment());
  207. Intent intent;
  208. if ("application/pdf".equals(mimeType)) {
  209. intent = new Intent(Intent.ACTION_VIEW, privacyUrl);
  210. DisplayUtils.startIntentIfAppAvailable(intent, this, R.string.no_pdf_app_available);
  211. } else {
  212. intent = new Intent(getApplicationContext(), ExternalSiteWebView.class);
  213. intent.putExtra(ExternalSiteWebView.EXTRA_TITLE,
  214. getResources().getString(R.string.privacy));
  215. intent.putExtra(ExternalSiteWebView.EXTRA_URL, privacyUrl.toString());
  216. intent.putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, false);
  217. intent.putExtra(ExternalSiteWebView.EXTRA_MENU_ITEM_ID, -1);
  218. }
  219. startActivity(intent);
  220. } catch (Exception e) {
  221. Log_OC.e(TAG, "Could not parse privacy url");
  222. preferenceCategoryAbout.removePreference(privacyPreference);
  223. }
  224. return true;
  225. });
  226. } else {
  227. preferenceCategoryAbout.removePreference(privacyPreference);
  228. }
  229. }
  230. // source code
  231. boolean sourcecodeEnabled = getResources().getBoolean(R.bool.sourcecode_enabled);
  232. Preference sourcecodePreference = findPreference("sourcecode");
  233. if (sourcecodePreference != null) {
  234. if (sourcecodeEnabled) {
  235. sourcecodePreference.setOnPreferenceClickListener(preference -> {
  236. DisplayUtils.startLinkIntent(this, R.string.sourcecode_url);
  237. return true;
  238. });
  239. } else {
  240. preferenceCategoryAbout.removePreference(sourcecodePreference);
  241. }
  242. }
  243. }
  244. private void setupMoreCategory(int accentColor, String appVersion) {
  245. PreferenceCategory preferenceCategoryMore = (PreferenceCategory) findPreference("more");
  246. preferenceCategoryMore.setTitle(ThemeUtils.getColoredTitle(getString(R.string.prefs_category_more),
  247. accentColor));
  248. setupCalendarPreference(preferenceCategoryMore);
  249. setupContactsBackupPreference(preferenceCategoryMore);
  250. setupE2EMnemonicPreference(preferenceCategoryMore);
  251. setupHelpPreference(preferenceCategoryMore);
  252. setupRecommendPreference(preferenceCategoryMore);
  253. setupFeedbackPreference(appVersion, preferenceCategoryMore);
  254. setupLoggingPreference(preferenceCategoryMore);
  255. setupImprintPreference(preferenceCategoryMore);
  256. loadExternalSettingLinks(preferenceCategoryMore);
  257. }
  258. private void setupImprintPreference(PreferenceCategory preferenceCategoryMore) {
  259. boolean imprintEnabled = getResources().getBoolean(R.bool.imprint_enabled);
  260. Preference pImprint = findPreference("imprint");
  261. if (pImprint != null) {
  262. if (imprintEnabled) {
  263. pImprint.setOnPreferenceClickListener(preference -> {
  264. String imprintWeb = getString(R.string.url_imprint);
  265. if (!imprintWeb.isEmpty()) {
  266. Uri uriUrl = Uri.parse(imprintWeb);
  267. Intent intent = new Intent(Intent.ACTION_VIEW, uriUrl);
  268. DisplayUtils.startIntentIfAppAvailable(intent, this, R.string.no_browser_available);
  269. }
  270. //ImprintDialog.newInstance(true).show(preference.get, "IMPRINT_DIALOG");
  271. return true;
  272. });
  273. } else {
  274. preferenceCategoryMore.removePreference(pImprint);
  275. }
  276. }
  277. }
  278. private void setupLoggingPreference(PreferenceCategory preferenceCategoryMore) {
  279. boolean loggerEnabled = getResources().getBoolean(R.bool.logger_enabled) || BuildConfig.DEBUG;
  280. Preference pLogger = findPreference("logger");
  281. if (pLogger != null) {
  282. if (loggerEnabled) {
  283. pLogger.setOnPreferenceClickListener(preference -> {
  284. Intent loggerIntent = new Intent(getApplicationContext(), LogHistoryActivity.class);
  285. startActivity(loggerIntent);
  286. return true;
  287. });
  288. } else {
  289. preferenceCategoryMore.removePreference(pLogger);
  290. }
  291. }
  292. }
  293. private void setupFeedbackPreference(String appVersion, PreferenceCategory preferenceCategoryMore) {
  294. boolean feedbackEnabled = getResources().getBoolean(R.bool.feedback_enabled);
  295. Preference pFeedback = findPreference("feedback");
  296. if (pFeedback != null) {
  297. if (feedbackEnabled) {
  298. pFeedback.setOnPreferenceClickListener(preference -> {
  299. String feedbackMail = getString(R.string.mail_feedback);
  300. String feedback = getText(R.string.prefs_feedback) + " - android v" + appVersion;
  301. Intent intent = new Intent(Intent.ACTION_SENDTO);
  302. intent.setDataAndType(Uri.parse(feedbackMail), "text/plain");
  303. intent.putExtra(Intent.EXTRA_SUBJECT, feedback);
  304. intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  305. if (intent.resolveActivity(getPackageManager()) != null) {
  306. startActivity(intent);
  307. } else {
  308. DisplayUtils.showSnackMessage(this, R.string.feedback_no_mail_app);
  309. }
  310. return true;
  311. });
  312. } else {
  313. preferenceCategoryMore.removePreference(pFeedback);
  314. }
  315. }
  316. }
  317. private void setupRecommendPreference(PreferenceCategory preferenceCategoryMore) {
  318. boolean recommendEnabled = getResources().getBoolean(R.bool.recommend_enabled);
  319. Preference pRecommend = findPreference("recommend");
  320. if (pRecommend != null) {
  321. if (recommendEnabled) {
  322. pRecommend.setOnPreferenceClickListener(preference -> {
  323. Intent intent = new Intent(Intent.ACTION_SEND);
  324. intent.setType("text/plain");
  325. intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  326. String appName = getString(R.string.app_name);
  327. String downloadUrlGooglePlayStore = getString(R.string.url_app_download);
  328. String downloadUrlFDroid = getString(R.string.fdroid_link);
  329. String downloadUrls = String.format(getString(R.string.recommend_urls),
  330. downloadUrlGooglePlayStore, downloadUrlFDroid);
  331. String recommendSubject = String.format(getString(R.string.recommend_subject), appName);
  332. String recommendText = String.format(getString(R.string.recommend_text),
  333. appName, downloadUrls);
  334. intent.putExtra(Intent.EXTRA_SUBJECT, recommendSubject);
  335. intent.putExtra(Intent.EXTRA_TEXT, recommendText);
  336. startActivity(intent);
  337. return true;
  338. });
  339. } else {
  340. preferenceCategoryMore.removePreference(pRecommend);
  341. }
  342. }
  343. }
  344. private void setupE2EMnemonicPreference(PreferenceCategory preferenceCategoryMore) {
  345. String mnemonic = mArbitraryDataProvider.getValue(mAccount.name, EncryptionUtils.MNEMONIC);
  346. Preference pMnemonic = findPreference("mnemonic");
  347. if (pMnemonic != null) {
  348. if (!mnemonic.isEmpty() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  349. if (DeviceCredentialUtils.areCredentialsAvailable(this)) {
  350. pMnemonic.setOnPreferenceClickListener(preference -> {
  351. Intent i = new Intent(MainApp.getAppContext(), RequestCredentialsActivity.class);
  352. i.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
  353. startActivityForResult(i, PassCodeManager.PASSCODE_ACTIVITY);
  354. return true;
  355. });
  356. } else {
  357. pMnemonic.setEnabled(false);
  358. pMnemonic.setSummary(R.string.prefs_e2e_no_device_credentials);
  359. }
  360. } else {
  361. preferenceCategoryMore.removePreference(pMnemonic);
  362. }
  363. }
  364. }
  365. private void setupHelpPreference(PreferenceCategory preferenceCategoryMore) {
  366. boolean helpEnabled = getResources().getBoolean(R.bool.help_enabled);
  367. Preference pHelp = findPreference("help");
  368. if (pHelp != null) {
  369. if (helpEnabled) {
  370. pHelp.setOnPreferenceClickListener(preference -> {
  371. String helpWeb = getString(R.string.url_help);
  372. if (!helpWeb.isEmpty()) {
  373. Uri uriUrl = Uri.parse(helpWeb);
  374. Intent intent = new Intent(Intent.ACTION_VIEW, uriUrl);
  375. DisplayUtils.startIntentIfAppAvailable(intent, this, R.string.no_browser_available);
  376. }
  377. return true;
  378. });
  379. } else {
  380. preferenceCategoryMore.removePreference(pHelp);
  381. }
  382. }
  383. }
  384. private void setupContactsBackupPreference(PreferenceCategory preferenceCategoryMore) {
  385. boolean contactsBackupEnabled = !getResources().getBoolean(R.bool.show_drawer_contacts_backup)
  386. && getResources().getBoolean(R.bool.contacts_backup);
  387. Preference pContactsBackup = findPreference("contacts");
  388. if (pContactsBackup != null) {
  389. if (contactsBackupEnabled) {
  390. pContactsBackup.setOnPreferenceClickListener(preference -> {
  391. Intent contactsIntent = new Intent(getApplicationContext(), ContactsPreferenceActivity.class);
  392. contactsIntent.putExtra(ContactsPreferenceActivity.EXTRA_SHOW_SIDEBAR, false);
  393. startActivity(contactsIntent);
  394. return true;
  395. });
  396. } else {
  397. preferenceCategoryMore.removePreference(pContactsBackup);
  398. }
  399. }
  400. }
  401. private void setupCalendarPreference(PreferenceCategory preferenceCategoryMore) {
  402. boolean calendarContactsEnabled = getResources().getBoolean(R.bool.davdroid_integration_enabled);
  403. Preference pCalendarContacts = findPreference("calendar_contacts");
  404. if (pCalendarContacts != null) {
  405. if (calendarContactsEnabled) {
  406. final Activity activity = this;
  407. pCalendarContacts.setOnPreferenceClickListener(preference -> {
  408. try {
  409. launchDavDroidLogin();
  410. } catch (Throwable t) {
  411. Log_OC.e(TAG, "Base Uri for account could not be resolved to call DAVdroid!", t);
  412. DisplayUtils.showSnackMessage(
  413. activity,
  414. R.string.prefs_calendar_contacts_address_resolve_error
  415. );
  416. }
  417. return true;
  418. });
  419. } else {
  420. preferenceCategoryMore.removePreference(pCalendarContacts);
  421. }
  422. }
  423. }
  424. private void setupDetailsCategory(int accentColor, PreferenceScreen preferenceScreen) {
  425. PreferenceCategory preferenceCategoryDetails = (PreferenceCategory) findPreference("details");
  426. preferenceCategoryDetails.setTitle(ThemeUtils.getColoredTitle(getString(R.string.prefs_category_details),
  427. accentColor));
  428. boolean fPassCodeEnabled = getResources().getBoolean(R.bool.passcode_enabled);
  429. boolean fDeviceCredentialsEnabled = getResources().getBoolean(R.bool.device_credentials_enabled);
  430. boolean fShowHiddenFilesEnabled = getResources().getBoolean(R.bool.show_hidden_files_enabled);
  431. boolean fSyncedFolderLightEnabled = getResources().getBoolean(R.bool.syncedFolder_light);
  432. setupLockPreference(preferenceCategoryDetails, fPassCodeEnabled, fDeviceCredentialsEnabled);
  433. setupHiddenFilesPreference(preferenceCategoryDetails, fShowHiddenFilesEnabled);
  434. if (!fPassCodeEnabled && !fDeviceCredentialsEnabled && !fShowHiddenFilesEnabled && fSyncedFolderLightEnabled) {
  435. preferenceScreen.removePreference(preferenceCategoryDetails);
  436. }
  437. }
  438. private void setupHiddenFilesPreference(PreferenceCategory preferenceCategoryDetails,
  439. boolean fShowHiddenFilesEnabled) {
  440. mShowHiddenFiles = (SwitchPreference) findPreference("show_hidden_files");
  441. if (fShowHiddenFilesEnabled) {
  442. mShowHiddenFiles.setOnPreferenceClickListener(preference -> {
  443. SharedPreferences appPrefs =
  444. PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
  445. SharedPreferences.Editor editor = appPrefs.edit();
  446. editor.putBoolean("show_hidden_files_pref", mShowHiddenFiles.isChecked());
  447. editor.apply();
  448. return true;
  449. });
  450. } else {
  451. preferenceCategoryDetails.removePreference(mShowHiddenFiles);
  452. }
  453. }
  454. private void setupLockPreference(PreferenceCategory preferenceCategoryDetails,
  455. boolean passCodeEnabled,
  456. boolean deviceCredentialsEnabled) {
  457. mLock = (ListPreference) findPreference(PREFERENCE_LOCK);
  458. if (mLock != null && (passCodeEnabled || deviceCredentialsEnabled)) {
  459. ArrayList<String> lockEntries = new ArrayList<>(3);
  460. lockEntries.add(getString(R.string.prefs_lock_none));
  461. lockEntries.add(getString(R.string.prefs_lock_using_passcode));
  462. lockEntries.add(getString(R.string.prefs_lock_using_device_credentials));
  463. ArrayList<String> lockValues = new ArrayList<>(3);
  464. lockValues.add(LOCK_NONE);
  465. lockValues.add(LOCK_PASSCODE);
  466. lockValues.add(LOCK_DEVICE_CREDENTIALS);
  467. if (!passCodeEnabled) {
  468. lockEntries.remove(1);
  469. lockValues.remove(1);
  470. } else if (!deviceCredentialsEnabled || Build.VERSION.SDK_INT < Build.VERSION_CODES.M ||
  471. !DeviceCredentialUtils.areCredentialsAvailable(getApplicationContext())) {
  472. lockEntries.remove(2);
  473. lockValues.remove(2);
  474. }
  475. String[] lockEntriesArr = new String[lockEntries.size()];
  476. lockEntriesArr = lockEntries.toArray(lockEntriesArr);
  477. String[] lockValuesArr = new String[lockValues.size()];
  478. lockValuesArr = lockValues.toArray(lockValuesArr);
  479. mLock.setEntries(lockEntriesArr);
  480. mLock.setEntryValues(lockValuesArr);
  481. mLock.setSummary(mLock.getEntry());
  482. mLock.setOnPreferenceChangeListener((preference, o) -> {
  483. pendingLock = LOCK_NONE;
  484. String oldValue = ((ListPreference) preference).getValue();
  485. String newValue = (String) o;
  486. if (!oldValue.equals(newValue)) {
  487. if (LOCK_NONE.equals(oldValue)) {
  488. enableLock(newValue);
  489. } else {
  490. pendingLock = newValue;
  491. disableLock(oldValue);
  492. }
  493. }
  494. return false;
  495. });
  496. } else {
  497. preferenceCategoryDetails.removePreference(mLock);
  498. }
  499. }
  500. private void setupAutoUploadCategory(int accentColor, PreferenceScreen preferenceScreen) {
  501. PreferenceCategory preferenceCategorySyncedFolders =
  502. (PreferenceCategory) findPreference("synced_folders_category");
  503. preferenceCategorySyncedFolders.setTitle(ThemeUtils.getColoredTitle(getString(R.string.drawer_synced_folders),
  504. accentColor));
  505. if (!getResources().getBoolean(R.bool.syncedFolder_light)) {
  506. preferenceScreen.removePreference(preferenceCategorySyncedFolders);
  507. } else {
  508. // Upload on WiFi
  509. final ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(getContentResolver());
  510. final SwitchPreference pUploadOnWifiCheckbox = (SwitchPreference) findPreference("synced_folder_on_wifi");
  511. pUploadOnWifiCheckbox.setChecked(
  512. arbitraryDataProvider.getBooleanValue(mAccount, SYNCED_FOLDER_LIGHT_UPLOAD_ON_WIFI));
  513. pUploadOnWifiCheckbox.setOnPreferenceClickListener(preference -> {
  514. arbitraryDataProvider.storeOrUpdateKeyValue(mAccount.name, SYNCED_FOLDER_LIGHT_UPLOAD_ON_WIFI,
  515. String.valueOf(pUploadOnWifiCheckbox.isChecked()));
  516. return true;
  517. });
  518. Preference pSyncedFolder = findPreference("synced_folders_configure_folders");
  519. if (pSyncedFolder != null) {
  520. if (getResources().getBoolean(R.bool.syncedFolder_light)
  521. && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  522. pSyncedFolder.setOnPreferenceClickListener(preference -> {
  523. Intent syncedFoldersIntent = new Intent(getApplicationContext(), SyncedFoldersActivity.class);
  524. syncedFoldersIntent.putExtra(SyncedFoldersActivity.EXTRA_SHOW_SIDEBAR, false);
  525. startActivity(syncedFoldersIntent);
  526. return true;
  527. });
  528. } else {
  529. preferenceCategorySyncedFolders.removePreference(pSyncedFolder);
  530. }
  531. }
  532. }
  533. }
  534. private void enableLock(String lock) {
  535. pendingLock = LOCK_NONE;
  536. if (LOCK_PASSCODE.equals(lock)) {
  537. Intent i = new Intent(getApplicationContext(), PassCodeActivity.class);
  538. i.setAction(PassCodeActivity.ACTION_REQUEST_WITH_RESULT);
  539. startActivityForResult(i, ACTION_REQUEST_PASSCODE);
  540. } else if (LOCK_DEVICE_CREDENTIALS.equals(lock)){
  541. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
  542. !DeviceCredentialUtils.areCredentialsAvailable(getApplicationContext())) {
  543. DisplayUtils.showSnackMessage(this, R.string.prefs_lock_device_credentials_not_setup);
  544. } else {
  545. DisplayUtils.showSnackMessage(this, R.string.prefs_lock_device_credentials_enabled);
  546. mLock.setValue(LOCK_DEVICE_CREDENTIALS);
  547. mLock.setSummary(mLock.getEntry());
  548. }
  549. }
  550. }
  551. private void disableLock(String lock) {
  552. if (LOCK_PASSCODE.equals(lock)) {
  553. Intent i = new Intent(getApplicationContext(), PassCodeActivity.class);
  554. i.setAction(PassCodeActivity.ACTION_CHECK_WITH_RESULT);
  555. startActivityForResult(i, ACTION_CONFIRM_PASSCODE);
  556. } else if (LOCK_DEVICE_CREDENTIALS.equals(lock)) {
  557. Intent i = new Intent(getApplicationContext(), RequestCredentialsActivity.class);
  558. startActivityForResult(i, ACTION_CONFIRM_DEVICE_CREDENTIALS);
  559. }
  560. }
  561. private void setupGeneralCategory(int accentColor) {
  562. PreferenceCategory preferenceCategoryGeneral = (PreferenceCategory) findPreference("general");
  563. preferenceCategoryGeneral.setTitle(ThemeUtils.getColoredTitle(getString(R.string.prefs_category_general),
  564. accentColor));
  565. mPrefStoragePath = (ListPreference) findPreference(PreferenceKeys.STORAGE_PATH);
  566. if (mPrefStoragePath != null) {
  567. StoragePoint[] storageOptions = DataStorageProvider.getInstance().getAvailableStoragePoints();
  568. String[] entries = new String[storageOptions.length];
  569. String[] values = new String[storageOptions.length];
  570. for (int i = 0; i < storageOptions.length; ++i) {
  571. entries[i] = storageOptions[i].getDescription();
  572. values[i] = storageOptions[i].getPath();
  573. }
  574. mPrefStoragePath.setEntries(entries);
  575. mPrefStoragePath.setEntryValues(values);
  576. mPrefStoragePath.setOnPreferenceChangeListener((preference, newValue) -> {
  577. String newPath = (String) newValue;
  578. if (mStoragePath.equals(newPath)) {
  579. return true;
  580. }
  581. StorageMigration storageMigration = new StorageMigration(this, mStoragePath, newPath);
  582. storageMigration.setStorageMigrationProgressListener(this);
  583. storageMigration.migrate();
  584. return false;
  585. });
  586. }
  587. loadStoragePath();
  588. }
  589. private String getAppVersion() {
  590. String temp;
  591. try {
  592. PackageInfo pkg = getPackageManager().getPackageInfo(getPackageName(), 0);
  593. temp = pkg.versionName;
  594. } catch (NameNotFoundException e) {
  595. temp = "";
  596. Log_OC.e(TAG, "Error while showing about dialog", e);
  597. }
  598. return temp;
  599. }
  600. @Override
  601. public boolean onOptionsItemSelected(MenuItem item) {
  602. finish();
  603. return super.onOptionsItemSelected(item);
  604. }
  605. private void setupActionBar() {
  606. ActionBar actionBar = getDelegate().getSupportActionBar();
  607. if (actionBar != null) {
  608. actionBar.setDisplayHomeAsUpEnabled(true);
  609. ThemeUtils.setColoredTitle(actionBar, getString(R.string.actionbar_settings), this);
  610. actionBar.setBackgroundDrawable(new ColorDrawable(ThemeUtils.primaryColor(this)));
  611. Drawable backArrow = getResources().getDrawable(R.drawable.ic_arrow_back);
  612. actionBar.setHomeAsUpIndicator(ThemeUtils.tintDrawable(backArrow, ThemeUtils.fontColor(this)));
  613. }
  614. Window window = getWindow();
  615. if (window != null) {
  616. window.getDecorView().setBackgroundDrawable(new ColorDrawable(ResourcesCompat
  617. .getColor(getResources(), R.color.background_color, null)));
  618. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  619. window.setStatusBarColor(ThemeUtils.primaryColor(this));
  620. }
  621. // For adding content description tag to a title field in the action bar
  622. int actionBarTitleId = getResources().getIdentifier("action_bar_title", "id", "android");
  623. View actionBarTitle = window.getDecorView().findViewById(actionBarTitleId);
  624. if (actionBarTitle != null) {
  625. actionBarTitle.setContentDescription(getString(R.string.actionbar_settings));
  626. }
  627. }
  628. }
  629. private void launchDavDroidLogin() {
  630. Intent davDroidLoginIntent = new Intent();
  631. davDroidLoginIntent.setClassName("at.bitfire.davdroid", "at.bitfire.davdroid.ui.setup.LoginActivity");
  632. if (getPackageManager().resolveActivity(davDroidLoginIntent, 0) != null) {
  633. // arguments
  634. if (mUri != null) {
  635. davDroidLoginIntent.putExtra("url", mUri.toString() + DAV_PATH);
  636. }
  637. davDroidLoginIntent.putExtra("username", AccountUtils.getAccountUsername(mAccount.name));
  638. startActivityForResult(davDroidLoginIntent, ACTION_REQUEST_CODE_DAVDROID_SETUP);
  639. } else {
  640. // DAVdroid not installed
  641. Intent installIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=at.bitfire.davdroid"));
  642. // launch market(s)
  643. if (installIntent.resolveActivity(getPackageManager()) != null) {
  644. startActivity(installIntent);
  645. } else {
  646. // no f-droid market app or Play store installed --> launch browser for f-droid url
  647. Intent downloadIntent = new Intent(Intent.ACTION_VIEW,
  648. Uri.parse("https://f-droid.org/repository/browse/?fdid=at.bitfire.davdroid"));
  649. DisplayUtils.startIntentIfAppAvailable(downloadIntent, this, R.string.no_browser_available);
  650. DisplayUtils.showSnackMessage(this, R.string.prefs_calendar_contacts_no_store_error);
  651. }
  652. }
  653. }
  654. private void setupBaseUri() {
  655. // retrieve and set user's base URI
  656. Thread t = new Thread(() -> {
  657. try {
  658. OwnCloudAccount ocAccount = new OwnCloudAccount(mAccount, MainApp.getAppContext());
  659. mUri = OwnCloudClientManagerFactory.getDefaultSingleton().getClientFor(ocAccount,
  660. getApplicationContext()).getBaseUri();
  661. } catch (Exception e) {
  662. Log_OC.e(TAG, "Error retrieving user's base URI", e);
  663. }
  664. });
  665. t.start();
  666. }
  667. @Override
  668. public boolean onCreateOptionsMenu(Menu menu) {
  669. super.onCreateOptionsMenu(menu);
  670. return true;
  671. }
  672. @Override
  673. protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  674. super.onActivityResult(requestCode, resultCode, data);
  675. if (requestCode == ACTION_REQUEST_PASSCODE && resultCode == RESULT_OK) {
  676. String passcode = data.getStringExtra(PassCodeActivity.KEY_PASSCODE);
  677. if (passcode != null && passcode.length() == 4) {
  678. SharedPreferences.Editor appPrefs = PreferenceManager
  679. .getDefaultSharedPreferences(getApplicationContext()).edit();
  680. for (int i = 1; i <= 4; ++i) {
  681. appPrefs.putString(PassCodeActivity.PREFERENCE_PASSCODE_D + i, passcode.substring(i - 1, i));
  682. }
  683. appPrefs.apply();
  684. mLock.setValue(LOCK_PASSCODE);
  685. mLock.setSummary(mLock.getEntry());
  686. DisplayUtils.showSnackMessage(this, R.string.pass_code_stored);
  687. }
  688. } else if (requestCode == ACTION_CONFIRM_PASSCODE && resultCode == RESULT_OK) {
  689. if (data.getBooleanExtra(PassCodeActivity.KEY_CHECK_RESULT, false)) {
  690. mLock.setValue(LOCK_NONE);
  691. mLock.setSummary(mLock.getEntry());
  692. DisplayUtils.showSnackMessage(this, R.string.pass_code_removed);
  693. if (!LOCK_NONE.equals(pendingLock)) {
  694. enableLock(pendingLock);
  695. }
  696. }
  697. } else if (requestCode == ACTION_REQUEST_CODE_DAVDROID_SETUP && resultCode == RESULT_OK) {
  698. DisplayUtils.showSnackMessage(this, R.string.prefs_calendar_contacts_sync_setup_successful);
  699. } else if (requestCode == ACTION_CONFIRM_DEVICE_CREDENTIALS && resultCode == RESULT_OK &&
  700. Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
  701. data.getIntExtra(RequestCredentialsActivity.KEY_CHECK_RESULT,
  702. RequestCredentialsActivity.KEY_CHECK_RESULT_FALSE) ==
  703. RequestCredentialsActivity.KEY_CHECK_RESULT_TRUE) {
  704. mLock.setValue(LOCK_NONE);
  705. mLock.setSummary(mLock.getEntry());
  706. DisplayUtils.showSnackMessage(this, R.string.credentials_disabled);
  707. if (!LOCK_NONE.equals(pendingLock)) {
  708. enableLock(pendingLock);
  709. }
  710. } else if (requestCode == PassCodeManager.PASSCODE_ACTIVITY && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  711. if (data == null) {
  712. DisplayUtils.showSnackMessage(this, "Error retrieving mnemonic!");
  713. } else {
  714. if (data.getIntExtra(RequestCredentialsActivity.KEY_CHECK_RESULT,
  715. RequestCredentialsActivity.KEY_CHECK_RESULT_FALSE) ==
  716. RequestCredentialsActivity.KEY_CHECK_RESULT_TRUE) {
  717. ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(getContentResolver());
  718. String mnemonic = arbitraryDataProvider.getValue(mAccount.name, EncryptionUtils.MNEMONIC);
  719. int accentColor = ThemeUtils.primaryAccentColor(this);
  720. AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.FallbackTheming_Dialog);
  721. builder.setTitle(ThemeUtils.getColoredTitle(getString(R.string.prefs_e2e_mnemonic), accentColor));
  722. builder.setMessage(mnemonic);
  723. builder.setPositiveButton(ThemeUtils.getColoredTitle(getString(R.string.common_ok), accentColor),
  724. (dialog, which) -> dialog.dismiss());
  725. builder.show();
  726. }
  727. }
  728. }
  729. }
  730. @Override
  731. @NonNull
  732. public MenuInflater getMenuInflater() {
  733. return getDelegate().getMenuInflater();
  734. }
  735. @Override
  736. public void setContentView(@LayoutRes int layoutResID) {
  737. getDelegate().setContentView(layoutResID);
  738. }
  739. @Override
  740. public void setContentView(View view) {
  741. getDelegate().setContentView(view);
  742. }
  743. @Override
  744. public void setContentView(View view, ViewGroup.LayoutParams params) {
  745. getDelegate().setContentView(view, params);
  746. }
  747. @Override
  748. public void addContentView(View view, ViewGroup.LayoutParams params) {
  749. getDelegate().addContentView(view, params);
  750. }
  751. @Override
  752. protected void onPostResume() {
  753. super.onPostResume();
  754. getDelegate().onPostResume();
  755. }
  756. @Override
  757. protected void onTitleChanged(CharSequence title, int color) {
  758. super.onTitleChanged(title, color);
  759. getDelegate().setTitle(title);
  760. }
  761. @Override
  762. public void onConfigurationChanged(Configuration newConfig) {
  763. super.onConfigurationChanged(newConfig);
  764. getDelegate().onConfigurationChanged(newConfig);
  765. }
  766. @Override
  767. protected void onPostCreate(Bundle savedInstanceState) {
  768. super.onPostCreate(savedInstanceState);
  769. getDelegate().onPostCreate(savedInstanceState);
  770. }
  771. @Override
  772. protected void onDestroy() {
  773. super.onDestroy();
  774. getDelegate().onDestroy();
  775. }
  776. @Override
  777. protected void onStop() {
  778. super.onStop();
  779. getDelegate().onStop();
  780. }
  781. public void invalidateOptionsMenu() {
  782. getDelegate().invalidateOptionsMenu();
  783. }
  784. private AppCompatDelegate getDelegate() {
  785. if (mDelegate == null) {
  786. mDelegate = AppCompatDelegate.create(this, null);
  787. }
  788. return mDelegate;
  789. }
  790. private void loadExternalSettingLinks(PreferenceCategory preferenceCategory) {
  791. if (getBaseContext().getResources().getBoolean(R.bool.show_external_links)) {
  792. ExternalLinksProvider externalLinksProvider = new ExternalLinksProvider(getContentResolver());
  793. for (final ExternalLink link : externalLinksProvider.getExternalLink(ExternalLinkType.SETTINGS)) {
  794. // only add if it does not exist, in case activity is re-used
  795. if (findPreference(link.id.toString()) == null) {
  796. Preference p = new Preference(this);
  797. p.setTitle(link.name);
  798. p.setKey(link.id.toString());
  799. p.setOnPreferenceClickListener(preference -> {
  800. Intent externalWebViewIntent = new Intent(getApplicationContext(), ExternalSiteWebView.class);
  801. externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_TITLE, link.name);
  802. externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_URL, link.url);
  803. externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_SHOW_SIDEBAR, false);
  804. externalWebViewIntent.putExtra(ExternalSiteWebView.EXTRA_MENU_ITEM_ID, link.id);
  805. startActivity(externalWebViewIntent);
  806. return true;
  807. });
  808. preferenceCategory.addPreference(p);
  809. }
  810. }
  811. }
  812. }
  813. /**
  814. * Save storage path
  815. */
  816. private void saveStoragePath(String newStoragePath) {
  817. SharedPreferences appPrefs =
  818. PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
  819. mStoragePath = newStoragePath;
  820. MainApp.setStoragePath(mStoragePath);
  821. SharedPreferences.Editor editor = appPrefs.edit();
  822. editor.putString(PreferenceKeys.STORAGE_PATH, mStoragePath);
  823. editor.apply();
  824. String storageDescription = DataStorageProvider.getInstance().getStorageDescriptionByPath(mStoragePath);
  825. mPrefStoragePath.setSummary(storageDescription);
  826. mPrefStoragePath.setValue(newStoragePath);
  827. }
  828. /**
  829. * Load storage path set on preferences
  830. */
  831. private void loadStoragePath() {
  832. SharedPreferences appPrefs =
  833. PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
  834. // Load storage path from shared preferences. Use private internal storage by default.
  835. mStoragePath = appPrefs.getString(PreferenceKeys.STORAGE_PATH,
  836. getApplicationContext().getFilesDir().getAbsolutePath());
  837. String storageDescription = DataStorageProvider.getInstance().getStorageDescriptionByPath(mStoragePath);
  838. mPrefStoragePath.setSummary(storageDescription);
  839. }
  840. @Override
  841. public void onStorageMigrationFinished(String storagePath, boolean succeed) {
  842. if (succeed) {
  843. saveStoragePath(storagePath);
  844. }
  845. }
  846. @Override
  847. public void onCancelMigration() {
  848. // Migration was canceled so we don't do anything
  849. }
  850. @Override
  851. public void returnVersion(Integer latestVersion) {
  852. FileActivity.showDevSnackbar(this, latestVersion, true);
  853. }
  854. }