ThemeUtils.java 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  1. /*
  2. * Nextcloud Android client application
  3. *
  4. * @author Tobias Kaminsky
  5. * @author Andy Scherzinger
  6. * Copyright (C) 2017 Tobias Kaminsky
  7. * Copyright (C) 2017 Nextcloud GmbH
  8. * Copyright (C) 2018 Andy Scherzinger
  9. *
  10. * This program is free software: you can redistribute it and/or modify
  11. * it under the terms of the GNU Affero General Public License as published by
  12. * the Free Software Foundation, either version 3 of the License, or
  13. * at your option) any later version.
  14. *
  15. * This program is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU Affero General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU Affero General Public License
  21. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. */
  23. package com.owncloud.android.utils;
  24. import android.accounts.Account;
  25. import android.app.Activity;
  26. import android.content.Context;
  27. import android.content.res.ColorStateList;
  28. import android.graphics.Color;
  29. import android.graphics.PorterDuff;
  30. import android.graphics.drawable.Drawable;
  31. import android.os.Build;
  32. import android.text.Spannable;
  33. import android.text.SpannableString;
  34. import android.text.Spanned;
  35. import android.text.style.ForegroundColorSpan;
  36. import android.view.View;
  37. import android.view.Window;
  38. import android.widget.EditText;
  39. import android.widget.ImageButton;
  40. import android.widget.ImageView;
  41. import android.widget.ProgressBar;
  42. import android.widget.SeekBar;
  43. import android.widget.TextView;
  44. import com.google.android.material.button.MaterialButton;
  45. import com.google.android.material.floatingactionbutton.FloatingActionButton;
  46. import com.google.android.material.snackbar.Snackbar;
  47. import com.google.android.material.textfield.TextInputLayout;
  48. import com.nextcloud.client.account.UserAccountManagerImpl;
  49. import com.owncloud.android.MainApp;
  50. import com.owncloud.android.R;
  51. import com.owncloud.android.datamodel.FileDataStorageManager;
  52. import com.owncloud.android.lib.common.utils.Log_OC;
  53. import com.owncloud.android.lib.resources.status.OCCapability;
  54. import com.owncloud.android.ui.activity.ToolbarActivity;
  55. import java.lang.reflect.Field;
  56. import androidx.annotation.ColorInt;
  57. import androidx.annotation.DrawableRes;
  58. import androidx.annotation.Nullable;
  59. import androidx.appcompat.app.ActionBar;
  60. import androidx.appcompat.app.AppCompatDelegate;
  61. import androidx.appcompat.widget.AppCompatCheckBox;
  62. import androidx.appcompat.widget.SearchView;
  63. import androidx.appcompat.widget.SwitchCompat;
  64. import androidx.core.content.ContextCompat;
  65. import androidx.core.content.res.ResourcesCompat;
  66. import androidx.core.graphics.ColorUtils;
  67. import androidx.core.graphics.drawable.DrawableCompat;
  68. import androidx.core.widget.CompoundButtonCompat;
  69. import androidx.fragment.app.FragmentActivity;
  70. import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
  71. /**
  72. * Utility class with methods for client side theming.
  73. */
  74. public final class ThemeUtils {
  75. private static final String TAG = ThemeUtils.class.getSimpleName();
  76. private static final int INDEX_LUMINATION = 2;
  77. private static final double MAX_LIGHTNESS = 0.92;
  78. public static final double LUMINATION_THRESHOLD = 0.8;
  79. private ThemeUtils() {
  80. // utility class -> private constructor
  81. }
  82. public static int primaryAccentColor(Context context) {
  83. OCCapability capability = getCapability(context);
  84. try {
  85. float adjust;
  86. if (darkTheme(context)) {
  87. if (AppCompatDelegate.getDefaultNightMode() == AppCompatDelegate.MODE_NIGHT_YES) {
  88. adjust = +0.5f;
  89. // return adjustLightness(adjust, Color.parseColor(capability.getServerColor()), -1);
  90. } else {
  91. adjust = +0.1f;
  92. }
  93. } else {
  94. adjust = -0.1f;
  95. }
  96. return adjustLightness(adjust, Color.parseColor(capability.getServerColor()), 0.35f);
  97. } catch (Exception e) {
  98. return context.getResources().getColor(R.color.color_accent);
  99. }
  100. }
  101. public static int primaryDarkColor(Context context) {
  102. return primaryDarkColor(null, context);
  103. }
  104. public static int primaryDarkColor(Account account, Context context) {
  105. OCCapability capability = getCapability(account, context);
  106. try {
  107. return adjustLightness(-0.2f, Color.parseColor(capability.getServerColor()), -1f);
  108. } catch (Exception e) {
  109. return context.getResources().getColor(R.color.primary_dark);
  110. }
  111. }
  112. public static int primaryColor(Context context) {
  113. return primaryColor(context, false);
  114. }
  115. public static int primaryColor(Context context, boolean replaceWhite) {
  116. return primaryColor(null, replaceWhite, context);
  117. }
  118. public static int primaryColor(Account account, boolean replaceWhite, Context context) {
  119. if (context == null) {
  120. return Color.GRAY;
  121. }
  122. try {
  123. int color = Color.parseColor(getCapability(account, context).getServerColor());
  124. if (replaceWhite && Color.WHITE == color) {
  125. return getNeutralGrey(context);
  126. } else {
  127. return color;
  128. }
  129. } catch (Exception e) {
  130. return context.getResources().getColor(R.color.primary);
  131. }
  132. }
  133. public static int getNeutralGrey(Context context) {
  134. return darkTheme(context) ? context.getResources().getColor(R.color.fg_contrast) :
  135. Color.GRAY;
  136. }
  137. public static int elementColor(Context context) {
  138. return elementColor(null, context);
  139. }
  140. @NextcloudServer(max = 12)
  141. public static int elementColor(Account account, Context context) {
  142. OCCapability capability = getCapability(account, context);
  143. try {
  144. return Color.parseColor(capability.getServerElementColor());
  145. } catch (Exception e) {
  146. int primaryColor;
  147. try {
  148. primaryColor = Color.parseColor(capability.getServerColor());
  149. } catch (Exception e1) {
  150. primaryColor = context.getResources().getColor(R.color.primary);
  151. }
  152. float[] hsl = colorToHSL(primaryColor);
  153. if (hsl[INDEX_LUMINATION] > LUMINATION_THRESHOLD) {
  154. return context.getResources().getColor(R.color.element_fallback_color);
  155. } else {
  156. return primaryColor;
  157. }
  158. }
  159. }
  160. public static boolean themingEnabled(Context context) {
  161. return getCapability(context).getServerColor() != null && !getCapability(context).getServerColor().isEmpty();
  162. }
  163. /**
  164. * @return int font color to use
  165. * adapted from https://github.com/nextcloud/server/blob/master/apps/theming/lib/Util.php#L90-L102
  166. */
  167. public static int fontColor(Context context, boolean replaceWhite) {
  168. if (AppCompatDelegate.getDefaultNightMode() == AppCompatDelegate.MODE_NIGHT_YES) {
  169. if (replaceWhite) {
  170. return Color.BLACK;
  171. } else {
  172. return Color.WHITE;
  173. }
  174. }
  175. try {
  176. return Color.parseColor(getCapability(context).getServerTextColor());
  177. } catch (Exception e) {
  178. if (darkTheme(context)) {
  179. return Color.WHITE;
  180. } else {
  181. return Color.BLACK;
  182. }
  183. }
  184. }
  185. public static int fontColor(Context context) {
  186. return fontColor(context, false);
  187. }
  188. /**
  189. * Tests if light color is set
  190. * @return true if primaryColor is lighter than MAX_LIGHTNESS
  191. */
  192. public static boolean lightTheme(Context context) {
  193. int primaryColor = primaryColor(context);
  194. float[] hsl = colorToHSL(primaryColor);
  195. return hsl[INDEX_LUMINATION] >= MAX_LIGHTNESS;
  196. }
  197. /**
  198. * Tests if dark color is set
  199. * @return true if dark theme -> e.g.use light font color, darker accent color
  200. */
  201. public static boolean darkTheme(Context context) {
  202. int primaryColor = primaryColor(context);
  203. float[] hsl = colorToHSL(primaryColor);
  204. return hsl[INDEX_LUMINATION] <= 0.55;
  205. }
  206. /**
  207. * Set color of title to white/black depending on background color
  208. *
  209. * @param actionBar actionBar to be used
  210. * @param title title to be shown
  211. */
  212. public static void setColoredTitle(@Nullable ActionBar actionBar, String title, Context context) {
  213. if (actionBar != null) {
  214. if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT) {
  215. actionBar.setTitle(title);
  216. } else {
  217. Spannable text = new SpannableString(title);
  218. text.setSpan(new ForegroundColorSpan(fontColor(context, !darkTheme(context))),
  219. 0,
  220. text.length(),
  221. Spannable.SPAN_INCLUSIVE_INCLUSIVE);
  222. actionBar.setTitle(text);
  223. }
  224. }
  225. }
  226. public static void setColoredTitle(@Nullable ActionBar actionBar, int titleId, Context context) {
  227. setColoredTitle(actionBar, context.getString(titleId), context);
  228. }
  229. /**
  230. * Set color of subtitle to white/black depending on background color
  231. *
  232. * @param actionBar actionBar to be used
  233. * @param title title to be shown
  234. */
  235. public static void setColoredSubtitle(@Nullable ActionBar actionBar, String title, Context context) {
  236. if (actionBar != null) {
  237. if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.KITKAT) {
  238. actionBar.setSubtitle(title);
  239. } else {
  240. Spannable text = new SpannableString(title);
  241. text.setSpan(new ForegroundColorSpan(fontColor(context)),
  242. 0,
  243. text.length(),
  244. Spannable.SPAN_INCLUSIVE_INCLUSIVE);
  245. actionBar.setSubtitle(text);
  246. }
  247. }
  248. }
  249. /**
  250. * For activities that do not use drawer, e.g. Settings, this can be used to correctly tint back button based on
  251. * theme
  252. *
  253. * @param supportActionBar
  254. */
  255. public static void tintBackButton(@Nullable ActionBar supportActionBar, Context context) {
  256. if (supportActionBar == null) {
  257. return;
  258. }
  259. Drawable backArrow = context.getResources().getDrawable(R.drawable.ic_arrow_back);
  260. supportActionBar.setHomeAsUpIndicator(ThemeUtils.tintDrawable(backArrow, ThemeUtils.fontColor(context)));
  261. }
  262. public static Spanned getColoredTitle(String title, int color) {
  263. Spannable text = new SpannableString(title);
  264. text.setSpan(new ForegroundColorSpan(color),
  265. 0,
  266. text.length(),
  267. Spannable.SPAN_INCLUSIVE_INCLUSIVE);
  268. return text;
  269. }
  270. public static String getDefaultDisplayNameForRootFolder(Context context) {
  271. OCCapability capability = getCapability(context);
  272. if (MainApp.isOnlyOnDevice()) {
  273. return MainApp.getAppContext().getString(R.string.drawer_item_on_device);
  274. } else {
  275. if (capability.getServerName() == null || capability.getServerName().isEmpty()) {
  276. return MainApp.getAppContext().getResources().getString(R.string.default_display_name_for_root_folder);
  277. } else {
  278. return capability.getServerName();
  279. }
  280. }
  281. }
  282. public static void setStatusBarColor(Activity activity, @ColorInt int color) {
  283. if (activity != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  284. activity.getWindow().setStatusBarColor(color);
  285. }
  286. }
  287. /**
  288. * Adjust lightness of given color
  289. *
  290. * @param lightnessDelta values -1..+1
  291. * @param color original color
  292. * @param threshold 0..1 as maximum value, -1 to disable
  293. * @return color adjusted by lightness
  294. */
  295. public static int adjustLightness(float lightnessDelta, int color, float threshold) {
  296. float[] hsl = colorToHSL(color);
  297. if (threshold == -1f) {
  298. hsl[INDEX_LUMINATION] += lightnessDelta;
  299. } else {
  300. hsl[INDEX_LUMINATION] = Math.min(hsl[INDEX_LUMINATION] + lightnessDelta, threshold);
  301. }
  302. return ColorUtils.HSLToColor(hsl);
  303. }
  304. private static float[] colorToHSL(int color) {
  305. float[] hsl = new float[3];
  306. ColorUtils.RGBToHSL(Color.red(color), Color.green(color), Color.blue(color), hsl);
  307. return hsl;
  308. }
  309. /**
  310. * sets the tinting of the given ImageButton's icon to color_accent.
  311. *
  312. * @param imageButton the image button who's icon should be colored
  313. */
  314. public static void colorImageButton(ImageButton imageButton, @ColorInt int color) {
  315. if (imageButton != null) {
  316. imageButton.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
  317. }
  318. }
  319. public static void colorEditText(EditText editText, int elementColor) {
  320. if (editText != null) {
  321. editText.setTextColor(elementColor);
  322. editText.getBackground().setColorFilter(elementColor, PorterDuff.Mode.SRC_ATOP);
  323. }
  324. }
  325. /**
  326. * sets the coloring of the given progress bar to given color.
  327. *
  328. * @param progressBar the progress bar to be colored
  329. * @param color the color to be used
  330. */
  331. public static void colorHorizontalProgressBar(ProgressBar progressBar, @ColorInt int color) {
  332. if (progressBar != null) {
  333. progressBar.getIndeterminateDrawable().setColorFilter(color, PorterDuff.Mode.SRC_IN);
  334. progressBar.getProgressDrawable().setColorFilter(color, PorterDuff.Mode.SRC_IN);
  335. }
  336. }
  337. /**
  338. * sets the coloring of the given progress bar's progress to given color.
  339. *
  340. * @param progressBar the progress bar to be colored
  341. * @param color the color to be used
  342. */
  343. public static void colorProgressBar(ProgressBar progressBar, @ColorInt int color) {
  344. if (progressBar != null) {
  345. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  346. progressBar.setProgressTintList(ColorStateList.valueOf(color));
  347. } else {
  348. ThemeUtils.colorHorizontalProgressBar(progressBar, color);
  349. }
  350. }
  351. }
  352. /**
  353. * sets the coloring of the given seek bar to color_accent.
  354. *
  355. * @param seekBar the seek bar to be colored
  356. */
  357. public static void colorHorizontalSeekBar(SeekBar seekBar, Context context) {
  358. int color = ThemeUtils.primaryAccentColor(context);
  359. colorHorizontalProgressBar(seekBar, color);
  360. seekBar.getThumb().setColorFilter(color, PorterDuff.Mode.SRC_IN);
  361. }
  362. /**
  363. * set the Nextcloud standard colors for the snackbar.
  364. *
  365. * @param context the context relevant for setting the color according to the context's theme
  366. * @param snackbar the snackbar to be colored
  367. */
  368. public static void colorSnackbar(Context context, Snackbar snackbar) {
  369. // Changing action button text color
  370. snackbar.setActionTextColor(ContextCompat.getColor(context, R.color.fg_inverse));
  371. }
  372. /**
  373. * Sets the color of the status bar to {@code color} on devices with OS version lollipop or higher.
  374. *
  375. * @param fragmentActivity fragment activity
  376. * @param color the color
  377. */
  378. public static void colorStatusBar(FragmentActivity fragmentActivity, @ColorInt int color) {
  379. Window window = fragmentActivity.getWindow();
  380. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && window != null) {
  381. window.setStatusBarColor(color);
  382. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
  383. View decor = window.getDecorView();
  384. if (lightTheme(fragmentActivity.getApplicationContext())) {
  385. decor.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
  386. } else {
  387. decor.setSystemUiVisibility(0);
  388. }
  389. }
  390. }
  391. }
  392. /**
  393. * Sets the color of the progressbar to {@code color} within the given toolbar.
  394. *
  395. * @param activity the toolbar activity instance
  396. * @param progressBarColor the color to be used for the toolbar's progress bar
  397. */
  398. public static void colorToolbarProgressBar(FragmentActivity activity, int progressBarColor) {
  399. if (activity instanceof ToolbarActivity) {
  400. ((ToolbarActivity) activity).setProgressBarBackgroundColor(progressBarColor);
  401. }
  402. }
  403. /**
  404. * Sets the color of the TextInputLayout to {@code color} for hint text and box stroke.
  405. *
  406. * @param textInputLayout the TextInputLayout instance
  407. * @param color the color to be used for the hint text and box stroke
  408. */
  409. public static void colorTextInputLayout(TextInputLayout textInputLayout, int color) {
  410. textInputLayout.setBoxStrokeColor(color);
  411. textInputLayout.setDefaultHintTextColor(new ColorStateList(
  412. new int[][]{
  413. new int[]{-android.R.attr.state_focused},
  414. new int[]{android.R.attr.state_focused},
  415. },
  416. new int[]{
  417. Color.GRAY,
  418. color
  419. }
  420. ));
  421. }
  422. public static void themeDialogActionButton(MaterialButton button) {
  423. if (button == null ) {
  424. return;
  425. }
  426. Context context = button.getContext();
  427. int accentColor = ThemeUtils.primaryAccentColor(button.getContext());
  428. int disabledColor = ContextCompat.getColor(context, R.color.disabled_text);
  429. button.setTextColor(new ColorStateList(
  430. new int[][]{
  431. new int[] { android.R.attr.state_enabled}, // enabled
  432. new int[] {-android.R.attr.state_enabled}, // disabled
  433. },
  434. new int[]{
  435. accentColor,
  436. disabledColor
  437. }
  438. ));
  439. }
  440. public static void themeEditText(Context context, EditText editText, boolean themedBackground) {
  441. if (editText == null) { return; }
  442. int color = ContextCompat.getColor(context, R.color.text_color);
  443. if (themedBackground) {
  444. if (darkTheme(context)) {
  445. color = ContextCompat.getColor(context, R.color.themed_fg);
  446. } else {
  447. color = ContextCompat.getColor(context, R.color.themed_fg_inverse);
  448. }
  449. }
  450. editText.setTextColor(color);
  451. editText.setHighlightColor(context.getResources().getColor(R.color.fg_contrast));
  452. setEditTextCursorColor(editText, color);
  453. setTextViewHandlesColor(context, editText, color);
  454. }
  455. /**
  456. * Theme search view
  457. *
  458. * @param searchView searchView to be changed
  459. * @param themedBackground true if background is themed, e.g. on action bar; false if background is white
  460. * @param context the app's context
  461. */
  462. public static void themeSearchView(SearchView searchView, boolean themedBackground, Context context) {
  463. // hacky as no default way is provided
  464. int fontColor = ThemeUtils.fontColor(context, !darkTheme(context));
  465. SearchView.SearchAutoComplete editText = searchView.findViewById(R.id.search_src_text);
  466. themeEditText(context, editText, themedBackground);
  467. ImageView closeButton = searchView.findViewById(androidx.appcompat.R.id.search_close_btn);
  468. closeButton.setColorFilter(fontColor);
  469. ImageView searchButton = searchView.findViewById(androidx.appcompat.R.id.search_button);
  470. searchButton.setColorFilter(fontColor);
  471. }
  472. public static void themeProgressBar(Context context, ProgressBar progressBar) {
  473. int color = ThemeUtils.primaryAccentColor(context);
  474. progressBar.getIndeterminateDrawable().setColorFilter(color, PorterDuff.Mode.SRC_IN);
  475. }
  476. public static void tintCheckbox(AppCompatCheckBox checkBox, int color) {
  477. CompoundButtonCompat.setButtonTintList(checkBox, new ColorStateList(
  478. new int[][]{
  479. new int[]{-android.R.attr.state_checked},
  480. new int[]{android.R.attr.state_checked},
  481. },
  482. new int[]{
  483. Color.GRAY,
  484. color
  485. }
  486. ));
  487. }
  488. public static void tintSwitch(SwitchCompat switchView, int color) {
  489. tintSwitch(switchView, color, false);
  490. }
  491. public static void tintSwitch(SwitchCompat switchView, int color, boolean colorText) {
  492. if (colorText) {
  493. switchView.setTextColor(color);
  494. }
  495. int trackColor = Color.argb(77, Color.red(color), Color.green(color), Color.blue(color));
  496. // setting the thumb color
  497. DrawableCompat.setTintList(switchView.getThumbDrawable(), new ColorStateList(
  498. new int[][]{new int[]{android.R.attr.state_checked}, new int[]{}},
  499. new int[]{color, Color.WHITE}));
  500. // setting the track color
  501. DrawableCompat.setTintList(switchView.getTrackDrawable(), new ColorStateList(
  502. new int[][]{new int[]{android.R.attr.state_checked}, new int[]{}},
  503. new int[]{trackColor, MainApp.getAppContext().getResources().getColor(R.color.switch_track_color_unchecked)}));
  504. }
  505. public static Drawable tintDrawable(@DrawableRes int id, int color) {
  506. Drawable drawable = ResourcesCompat.getDrawable(MainApp.getAppContext().getResources(), id, null);
  507. return tintDrawable(drawable, color);
  508. }
  509. @Nullable
  510. public static Drawable tintDrawable(Drawable drawable, int color) {
  511. if (drawable != null) {
  512. Drawable wrap = DrawableCompat.wrap(drawable);
  513. wrap.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
  514. return wrap;
  515. }
  516. return null;
  517. }
  518. public static String colorToHexString(int color) {
  519. return String.format("#%06X", 0xFFFFFF & color);
  520. }
  521. public static void tintFloatingActionButton(FloatingActionButton button, @DrawableRes int
  522. drawable, Context context) {
  523. button.setBackgroundTintList(ColorStateList.valueOf(ThemeUtils.primaryColor(context)));
  524. button.setRippleColor(ThemeUtils.primaryDarkColor(context));
  525. button.setImageDrawable(ThemeUtils.tintDrawable(drawable, ThemeUtils.fontColor(context)));
  526. }
  527. private static OCCapability getCapability(Context context) {
  528. return getCapability(null, context);
  529. }
  530. private static OCCapability getCapability(Account acc, Context context) {
  531. Account account = null;
  532. if (acc != null) {
  533. account = acc;
  534. } else if (context != null) {
  535. // TODO: refactor when dark theme work is completed
  536. account = UserAccountManagerImpl.fromContext(context).getCurrentAccount();
  537. }
  538. if (account != null) {
  539. FileDataStorageManager storageManager = new FileDataStorageManager(account, context.getContentResolver());
  540. return storageManager.getCapability(account.name);
  541. } else {
  542. return new OCCapability();
  543. }
  544. }
  545. /**
  546. * Lifted from SO.
  547. * FindBugs surpressed because of lack of public API to alter the cursor color.
  548. *
  549. * @param editText TextView to be styled
  550. * @param color The desired cursor colour
  551. * @see <a href="https://stackoverflow.com/a/52564925">StackOverflow url</a>
  552. */
  553. @SuppressFBWarnings
  554. public static void setEditTextCursorColor(EditText editText, int color) {
  555. try {
  556. // Get the cursor resource id
  557. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {//set differently in Android P (API 28)
  558. Field field = TextView.class.getDeclaredField("mCursorDrawableRes");
  559. field.setAccessible(true);
  560. int drawableResId = field.getInt(editText);
  561. // Get the editor
  562. field = TextView.class.getDeclaredField("mEditor");
  563. field.setAccessible(true);
  564. Object editor = field.get(editText);
  565. // Get the drawable and set a color filter
  566. Drawable drawable = ContextCompat.getDrawable(editText.getContext(), drawableResId);
  567. drawable.setColorFilter(color, PorterDuff.Mode.SRC_IN);
  568. // Set the drawables
  569. field = editor.getClass().getDeclaredField("mDrawableForCursor");
  570. field.setAccessible(true);
  571. field.set(editor, drawable);
  572. } else {
  573. Field field = TextView.class.getDeclaredField("mCursorDrawableRes");
  574. field.setAccessible(true);
  575. int drawableResId = field.getInt(editText);
  576. // Get the editor
  577. field = TextView.class.getDeclaredField("mEditor");
  578. field.setAccessible(true);
  579. Object editor = field.get(editText);
  580. // Get the drawable and set a color filter
  581. Drawable drawable = ContextCompat.getDrawable(editText.getContext(), drawableResId);
  582. drawable.setColorFilter(color, PorterDuff.Mode.SRC_IN);
  583. Drawable[] drawables = {drawable, drawable};
  584. // Set the drawables
  585. field = editor.getClass().getDeclaredField("mCursorDrawable");
  586. field.setAccessible(true);
  587. field.set(editor, drawables);
  588. }
  589. } catch (Exception exception) {
  590. // we do not log this
  591. }
  592. }
  593. /**
  594. * Set the color of the handles when you select text in a
  595. * {@link android.widget.EditText} or other view that extends {@link TextView}.
  596. * FindBugs surpressed because of lack of public API to alter the {@link TextView} handles color.
  597. *
  598. * @param view
  599. * The {@link TextView} or a {@link View} that extends {@link TextView}.
  600. * @param color
  601. * The color to set for the text handles
  602. *
  603. * @see <a href="https://gist.github.com/jaredrummler/2317620559d10ac39b8218a1152ec9d4">External reference</a>
  604. */
  605. @SuppressFBWarnings
  606. private static void setTextViewHandlesColor(Context context, TextView view, int color) {
  607. try {
  608. Field editorField = TextView.class.getDeclaredField("mEditor");
  609. if (!editorField.isAccessible()) {
  610. editorField.setAccessible(true);
  611. }
  612. Object editor = editorField.get(view);
  613. Class<?> editorClass = editor.getClass();
  614. String[] handleNames = {"mSelectHandleLeft", "mSelectHandleRight", "mSelectHandleCenter"};
  615. String[] resNames = {"mTextSelectHandleLeftRes", "mTextSelectHandleRightRes", "mTextSelectHandleRes"};
  616. for (int i = 0; i < handleNames.length; i++) {
  617. Field handleField = editorClass.getDeclaredField(handleNames[i]);
  618. if (!handleField.isAccessible()) {
  619. handleField.setAccessible(true);
  620. }
  621. Drawable handleDrawable = (Drawable) handleField.get(editor);
  622. if (handleDrawable == null) {
  623. Field resField = TextView.class.getDeclaredField(resNames[i]);
  624. if (!resField.isAccessible()) {
  625. resField.setAccessible(true);
  626. }
  627. int resId = resField.getInt(view);
  628. handleDrawable = ContextCompat.getDrawable(context, resId);
  629. }
  630. if (handleDrawable != null) {
  631. Drawable drawable = handleDrawable.mutate();
  632. drawable.setColorFilter(color, PorterDuff.Mode.SRC_IN);
  633. handleField.set(editor, drawable);
  634. }
  635. }
  636. } catch (Exception e) {
  637. Log_OC.e(TAG, "Error setting TextView handles color", e);
  638. }
  639. }
  640. }