diff --git a/app/build.gradle b/app/build.gradle index 3127bdafd..a524923f3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,7 +13,7 @@ android { javaCompileOptions { annotationProcessorOptions { - arguments = [ eventBusIndex : 'fr.gaulupeau.apps.Poche.EventBusIndex' ] + arguments = [eventBusIndex: 'fr.gaulupeau.apps.Poche.EventBusIndex'] } } } @@ -55,8 +55,10 @@ greendao { dependencies { implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'androidx.media:media:1.3.1' + implementation "androidx.webkit:webkit:1.4.0" implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0' implementation 'androidx.recyclerview:recyclerview:1.2.0' + implementation 'androidx.preference:preference:1.1.1' implementation 'com.google.android.material:material:1.3.0' implementation 'org.greenrobot:eventbus:3.2.0' implementation 'org.greenrobot:greendao:3.3.0' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1cf7969ae..b7a6f8f10 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ @@ -22,20 +21,18 @@ android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" - android:theme="@style/LightTheme" + android:theme="@style/Theme.App" android:networkSecurityConfig="@xml/network_security_config"> + android:theme="@style/Theme.Splash"> - + @@ -55,7 +52,7 @@ android:autoRemoveFromRecents="true" android:excludeFromRecents="true" android:noHistory="true" - android:theme="@style/ProxyTheme"> + android:theme="@style/Theme.App.ProxyTheme"> @@ -69,8 +66,7 @@ android:autoRemoveFromRecents="true" android:enabled="false" android:excludeFromRecents="true" - android:noHistory="true" - android:theme="@style/ProxyTheme"> + android:noHistory="true"> @@ -96,8 +92,7 @@ + android:name="fr.gaulupeau.apps.Poche.ui.EditAddedArticleActivity" /> diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java b/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java index a309162f2..fe63f7e58 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/data/Settings.java @@ -88,7 +88,7 @@ public void initPreferences() { } else { // preferences are not migrated -- set some default values boolean isOnyxDevice = Build.MANUFACTURER.equals("Onyx"); boolean isEreader = isOnyxDevice || Build.MODEL.equals("NOOK"); - Themes.Theme theme = isEreader ? Themes.Theme.E_INK : Themes.Theme.LIGHT; + Themes.Theme theme = isEreader ? Themes.Theme.E_INK : Themes.Theme.FOLLOW_SYSTEM; prefEditor.putString(context.getString(R.string.pref_key_ui_theme), theme.toString()); prefEditor.putBoolean(context.getString(R.string.pref_key_ui_onyxworkaround_enabled), isOnyxDevice); } @@ -441,7 +441,7 @@ public Themes.Theme getTheme() { } catch(IllegalArgumentException ignored) {} } - return theme != null ? theme : Themes.Theme.LIGHT; + return theme != null ? theme : Themes.Theme.FOLLOW_SYSTEM; } public void setTheme(Themes.Theme theme) { diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/events/EventProcessor.java b/app/src/main/java/fr/gaulupeau/apps/Poche/events/EventProcessor.java index 578d38c32..27c685e69 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/events/EventProcessor.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/events/EventProcessor.java @@ -169,7 +169,7 @@ public void onUpdateArticlesStartedEvent(UpdateArticlesStartedEvent event) { detailedMessage = prependAppName(detailedMessage); NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, CHANNEL_ID_SYNC) - .setSmallIcon(R.drawable.ic_action_refresh) + .setSmallIcon(R.drawable.ic_sync) .setContentTitle(context.getString(R.string.notification_updatingArticles)) .setContentText(detailedMessage) .setOngoing(true); @@ -209,7 +209,7 @@ public void onSweepDeletedArticlesStartedEvent(SweepDeletedArticlesStartedEvent Context context = getContext(); NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, CHANNEL_ID_SYNC) - .setSmallIcon(R.drawable.ic_action_refresh) + .setSmallIcon(R.drawable.ic_sync) .setContentTitle(context.getString(R.string.notification_sweepingDeletedArticles)) .setOngoing(true); @@ -253,7 +253,7 @@ public void onFetchImagesProgressEvent(FetchImagesProgressEvent event) { Context context = getContext(); NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, CHANNEL_ID_SYNC) - .setSmallIcon(R.drawable.ic_action_refresh) + .setSmallIcon(R.drawable.ic_sync) .setContentTitle(context.getString(R.string.notification_downloadingImages)) .setOngoing(true); @@ -306,7 +306,7 @@ public void onSyncQueueProgressEvent(SyncQueueProgressEvent event) { Context context = getContext(); notificationBuilder = new NotificationCompat.Builder(context, CHANNEL_ID_SYNC) - .setSmallIcon(R.drawable.ic_action_refresh) + .setSmallIcon(R.drawable.ic_sync) .setContentTitle(getContext().getString(R.string.notification_syncingQueue)) .setOngoing(true); diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/service/MainService.java b/app/src/main/java/fr/gaulupeau/apps/Poche/service/MainService.java index 22e520a33..934ca8e2d 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/service/MainService.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/service/MainService.java @@ -24,7 +24,7 @@ protected Notification getForegroundNotification() { return new NotificationCompat.Builder( this, NotificationsHelper.CHANNEL_ID_BACKGROUND_OPERATIONS) - .setSmallIcon(R.drawable.ic_action_refresh) + .setSmallIcon(R.drawable.ic_sync) .setContentTitle(getString(R.string.notification_backgroundOperations)) .build(); } diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/service/SecondaryService.java b/app/src/main/java/fr/gaulupeau/apps/Poche/service/SecondaryService.java index b0eb12055..90bc14ece 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/service/SecondaryService.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/service/SecondaryService.java @@ -24,7 +24,7 @@ protected Notification getForegroundNotification() { return new NotificationCompat.Builder( this, NotificationsHelper.CHANNEL_ID_BACKGROUND_OPERATIONS) - .setSmallIcon(R.drawable.ic_action_refresh) + .setSmallIcon(R.drawable.ic_sync) .setContentTitle(getString(R.string.notification_backgroundOperations)) .build(); } diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/BaseActionBarActivity.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/BaseActionBarActivity.java index e872b10b6..751902526 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/BaseActionBarActivity.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/BaseActionBarActivity.java @@ -3,7 +3,14 @@ import android.os.Bundle; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; + import android.view.MenuItem; +import android.view.View; + +import com.google.android.material.appbar.MaterialToolbar; + +import fr.gaulupeau.apps.InThePoche.R; public abstract class BaseActionBarActivity extends AppCompatActivity { @@ -11,8 +18,6 @@ public abstract class BaseActionBarActivity extends AppCompatActivity { protected void onCreate(Bundle savedInstanceState) { Themes.applyTheme(this); super.onCreate(savedInstanceState); - - addBackButtonToActionBar(); } @Override @@ -22,9 +27,8 @@ protected void onStart() { Themes.checkTheme(this); } - protected void addBackButtonToActionBar() { - ActionBar actionBar = getSupportActionBar(); - if(actionBar != null) actionBar.setDisplayHomeAsUpEnabled(true); + protected void addBackButtonToActionBar(Toolbar toolbar) { + toolbar.setNavigationOnClickListener(v -> onBackPressed()); } protected void hideBackButtonFromActionBar() { diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/MainActivity.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/MainActivity.java index 4dc5d7bd9..87fb98d25 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/MainActivity.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/MainActivity.java @@ -17,7 +17,6 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; @@ -33,6 +32,7 @@ import androidx.fragment.app.Fragment; import com.google.android.material.navigation.NavigationView; +import com.google.android.material.progressindicator.LinearProgressIndicator; import com.mikepenz.aboutlibraries.Libs; import com.mikepenz.aboutlibraries.LibsBuilder; @@ -84,7 +84,7 @@ public class MainActivity extends AppCompatActivity private ConfigurationTestHelper configurationTestHelper; - private ProgressBar progressBar; + private LinearProgressIndicator progressBar; private NavigationView navigationView; private TextView lastUpdateTimeView; @@ -136,22 +136,6 @@ protected void onCreate(Bundle savedInstanceState) { if (headerView != null) { lastUpdateTimeView = headerView.findViewById(R.id.lastUpdateTime); } - - // Set different colors for items in the navigation bar in dark (high contrast) theme - if (Themes.getCurrentTheme() == Themes.Theme.DARK_CONTRAST) { - @SuppressLint("ResourceType") XmlResourceParser parser = getResources().getXml(R.color.dark_contrast_menu_item); - try { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - navigationView.setItemTextColor(ColorStateList.createFromXml(getResources(), parser, getTheme())); - navigationView.setItemIconTintList(ColorStateList.createFromXml(getResources(), parser, getTheme())); - } else { - navigationView.setItemTextColor(ColorStateList.createFromXml(getResources(), parser)); - navigationView.setItemIconTintList(ColorStateList.createFromXml(getResources(), parser)); - } - } catch (XmlPullParserException | IOException e) { - Log.e(TAG, "onCreate()", e); - } - } } DrawerLayout drawer = findViewById(R.id.drawer_layout); @@ -468,23 +452,11 @@ public boolean onNavigationItemSelected(@NonNull MenuItem item) { break; case R.id.nav_about: - Libs.ActivityStyle style; - switch (Themes.getCurrentTheme()) { - case DARK: - case DARK_CONTRAST: - style = Libs.ActivityStyle.DARK; - break; - - default: - style = Libs.ActivityStyle.LIGHT_DARK_TOOLBAR; - break; - } CharSequence aboutCharSequence = getText(R.string.aboutText); String aboutString = aboutCharSequence instanceof Spanned ? Html.toHtml((Spanned) aboutCharSequence) : aboutCharSequence.toString(); new LibsBuilder() - .withActivityStyle(style) .withAboutIconShown(true) .withAboutVersionShown(true) .withAboutDescription(aboutString) @@ -546,8 +518,10 @@ public void onUpdateArticlesProgressEvent(UpdateArticlesProgressEvent event) { if (progressBar != null) { progressBar.setIndeterminate(false); - progressBar.setMax(event.getTotal()); - progressBar.setProgress(event.getCurrent()); + if (event.getTotal() > 0) { + progressBar.setMax(event.getTotal()); + progressBar.setProgressCompat(event.getCurrent(), true); + } } } @@ -585,7 +559,7 @@ private void updateStateChanged(boolean started) { updateRunning = started; if (progressBar != null) { - progressBar.setVisibility(started ? View.VISIBLE : View.GONE); + progressBar.setVisibility(started ? View.VISIBLE : View.INVISIBLE); progressBar.setIndeterminate(true); } } diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ManageArticleTagsActivity.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ManageArticleTagsActivity.java index df78bf4a9..8888f0058 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ManageArticleTagsActivity.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ManageArticleTagsActivity.java @@ -15,6 +15,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.appcompat.widget.Toolbar; import androidx.recyclerview.widget.DiffUtil; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; @@ -87,6 +88,10 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { setContentView(R.layout.activity_manage_article_tags); + Toolbar toolbar = findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + toolbar.setNavigationOnClickListener(v -> onBackPressed()); + String[] currentTagsArray = null; String text; if (savedInstanceState != null) { diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ReadArticleActivity.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ReadArticleActivity.java index 72646cf72..83a7ea6a9 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ReadArticleActivity.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/ReadArticleActivity.java @@ -2,6 +2,7 @@ import android.annotation.SuppressLint; import android.content.Intent; +import android.content.res.Configuration; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; @@ -33,6 +34,10 @@ import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.widget.Toolbar; +import androidx.core.widget.NestedScrollView; +import androidx.webkit.WebSettingsCompat; +import androidx.webkit.WebViewFeature; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; @@ -129,7 +134,7 @@ public class ReadArticleActivity extends BaseActionBarActivity { private boolean annotationsEnabled; private boolean onyxWorkaroundEnabled; - private ScrollView scrollView; + private NestedScrollView scrollView; private View scrollViewLastChild; private WebView webViewContent; private TextView loadingPlaceholder; @@ -180,6 +185,36 @@ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.article); + Toolbar toolbar = findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + toolbar.setNavigationOnClickListener(v -> onBackPressed()); + toolbar.setOnMenuItemClickListener(item -> { + switch (item.getItemId()) { + case R.id.menuArticleMarkAsRead: + case R.id.menuArticleMarkAsUnread: + markAsReadAndClose(); + return true; + + case R.id.menuDelete: + deleteArticle(); + return true; + + case R.id.menuIncreaseFontSize: + changeFontSize(true); + return true; + + case R.id.menuDecreaseFontSize: + changeFontSize(false); + return true; + + case R.id.menuTTS: + toggleTTS(true); + return true; + } + + return articleActionsHelper.handleContextItemSelected(this, article, item); + }); + if (fullscreenArticleView) { ActionBar actionBar = getSupportActionBar(); if (actionBar != null) actionBar.hide(); @@ -307,36 +342,6 @@ public boolean onCreateOptionsMenu(Menu menu) { return true; } - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.menuArticleMarkAsRead: - case R.id.menuArticleMarkAsUnread: - markAsReadAndClose(); - return true; - - case R.id.menuDelete: - deleteArticle(); - return true; - - case R.id.menuIncreaseFontSize: - changeFontSize(true); - return true; - - case R.id.menuDecreaseFontSize: - changeFontSize(false); - return true; - - case R.id.menuTTS: - toggleTTS(true); - return true; - } - - if (articleActionsHelper.handleContextItemSelected(this, article, item)) return true; - - return super.onOptionsItemSelected(item); - } - @Override public boolean dispatchKeyEvent(KeyEvent event) { int code = event.getKeyCode(); @@ -627,6 +632,7 @@ private void initWebView() { webViewSettings.setAllowFileAccess(true); } + applyDarkTheme(webViewSettings); initTtsController(); initAnnotationController(); @@ -796,6 +802,22 @@ public boolean onSingleTapConfirmed(MotionEvent e) { webViewContent.setOnTouchListener((v, event) -> gestureDetector.onTouchEvent(event)); } + private void applyDarkTheme(WebSettings settings) { + if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { + switch (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) { + case Configuration.UI_MODE_NIGHT_YES: + // enable force dark + WebSettingsCompat.setForceDark(settings, WebSettingsCompat.FORCE_DARK_ON); + break; + case Configuration.UI_MODE_NIGHT_NO: + case Configuration.UI_MODE_NIGHT_UNDEFINED: + // disable force dark + WebSettingsCompat.setForceDark(settings, WebSettingsCompat.FORCE_DARK_OFF); + break; + } + } + } + private void initTtsController() { // add the controller now even if TTS is not used, // otherwise it won't be possible to enable TTS without content reloading diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/Themes.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/Themes.java index 1bc3ec14b..7088d4b63 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/Themes.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/Themes.java @@ -1,8 +1,11 @@ package fr.gaulupeau.apps.Poche.ui; -import android.app.Activity; import androidx.annotation.StringRes; import androidx.annotation.StyleRes; +import androidx.appcompat.app.AppCompatDelegate; + +import android.app.Activity; +import android.os.Build; import java.util.Map; import java.util.WeakHashMap; @@ -35,59 +38,90 @@ public static void applyTheme(Activity activity) { public static void applyTheme(Activity activity, boolean actionBar) { activity.setTheme(actionBar ? theme.getResId() : theme.getNoActionBarResId()); appliedThemes.put(activity, theme); + applyDarkTheme(); + } + + public static void applyDarkTheme() { + if (theme.name().toLowerCase().contains("light")) { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); + } else if (theme.name().toLowerCase().contains("dark")) { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); + } else { + // `Set by Battery Saver` for Q above (inclusive), `Use system default` for Q below + // https://medium.com/androiddevelopers/appcompat-v23-2-daynight-d10f90c83e94 + AppCompatDelegate.setDefaultNightMode(Build.VERSION.SDK_INT < Build.VERSION_CODES.Q ? + AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY : AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM); + } } - public static void applyDialogTheme(Activity activity) { + public static void applyDialogTheme(final Activity activity) { activity.setTheme(theme.getDialogResId()); appliedThemes.put(activity, theme); + applyDarkTheme(); } public static void checkTheme(Activity activity) { Theme appliedTheme = appliedThemes.get(activity); - if(appliedTheme != theme) activity.recreate(); + if (appliedTheme != theme) { + activity.recreate(); + } } public enum Theme { + FOLLOW_SYSTEM( + R.string.themeName_follow_system, + R.style.Theme_App, + R.style.Theme_App_NoActionBar, + R.style.Theme_App_DialogTheme + ), + + CONTRAST( + R.string.themeName_contrast, + R.style.Theme_App_Contrast, + R.style.Theme_App_Contrast_NoActionBar, + R.style.Theme_App_DialogTheme + ), + LIGHT( R.string.themeName_light, - R.style.LightTheme, - R.style.LightTheme_NoActionBar, - R.style.DialogTheme + R.style.Theme_App, + R.style.Theme_App_NoActionBar, + R.style.Theme_App_DialogTheme ), LIGHT_CONTRAST( R.string.themeName_light_contrast, - R.style.LightThemeContrast, - R.style.LightThemeContrast_NoActionBar, - R.style.DialogTheme + R.style.Theme_App_Contrast, + R.style.Theme_App_Contrast_NoActionBar, + R.style.Theme_App_DialogTheme ), E_INK( R.string.themeName_eink, - R.style.LightThemeContrast, - R.style.LightThemeContrast_NoActionBar, - R.style.DialogTheme + R.style.Theme_App_Contrast, + R.style.Theme_App_Contrast_NoActionBar, + R.style.Theme_App_DialogTheme ), DARK( R.string.themeName_dark, - R.style.DarkTheme, - R.style.DarkTheme_NoActionBar, - R.style.DialogThemeDark + R.style.Theme_App, + R.style.Theme_App_NoActionBar, + R.style.Theme_App_DialogTheme ), DARK_CONTRAST( R.string.themeName_dark_contrast, - R.style.DarkThemeContrast, - R.style.DarkThemeContrast_NoActionBar, - R.style.DialogThemeDark + R.style.Theme_App_Contrast, + R.style.Theme_App_Contrast_NoActionBar, + R.style.Theme_App_DialogTheme ), SOLARIZED( R.string.themeName_solarized, - R.style.SolarizedTheme, - R.style.SolarizedTheme_NoActionBar, - R.style.DialogTheme + R.style.Theme_App_Solarized, + R.style.Theme_App_Solarized_NoActionBar, + R.style.Theme_App_DialogTheme ); private int nameId; diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/preferences/ConnectionWizardActivity.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/preferences/ConnectionWizardActivity.java index c97f91fae..1cdafe4aa 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/preferences/ConnectionWizardActivity.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/preferences/ConnectionWizardActivity.java @@ -17,6 +17,7 @@ import android.widget.Toast; import androidx.annotation.Nullable; +import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentTransaction; @@ -79,6 +80,12 @@ public static void runWizard(Context context, boolean skipWelcome, boolean showS protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + setContentView(R.layout.activity_settings); + + Toolbar toolbar = findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + toolbar.setNavigationOnClickListener(v -> onBackPressed()); + if(savedInstanceState == null) { Intent intent = getIntent(); Bundle bundle = new Bundle(); @@ -302,7 +309,7 @@ private void next(String currentPage, Bundle bundle, boolean noBackStack) { goToFragment.setArguments(bundle); FragmentTransaction ft = getSupportFragmentManager().beginTransaction() - .replace(android.R.id.content, goToFragment) + .replace(R.id.content, goToFragment) .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); if(!noBackStack && !PAGE_NONE.equals(currentPage)) ft.addToBackStack(null); ft.commitAllowingStateLoss(); diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/preferences/SettingsActivity.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/preferences/SettingsActivity.java index f408fe6ec..1e88e969c 100644 --- a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/preferences/SettingsActivity.java +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/preferences/SettingsActivity.java @@ -1,50 +1,11 @@ package fr.gaulupeau.apps.Poche.ui.preferences; -import android.annotation.SuppressLint; -import android.app.Activity; -import android.app.AlarmManager; -import android.content.DialogInterface; -import android.content.SharedPreferences; import android.os.Bundle; -import android.preference.CheckBoxPreference; -import android.preference.EditTextPreference; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.PreferenceFragment; -import android.text.TextUtils; -import android.util.Log; -import android.view.KeyEvent; -import android.view.View; -import android.widget.TextView; -import android.widget.Toast; -import androidx.appcompat.app.AlertDialog; - -import java.io.File; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import androidx.appcompat.widget.Toolbar; import fr.gaulupeau.apps.InThePoche.R; -import fr.gaulupeau.apps.Poche.App; -import fr.gaulupeau.apps.Poche.data.DbConnection; -import fr.gaulupeau.apps.Poche.data.PreferenceKeysMap; -import fr.gaulupeau.apps.Poche.data.QueueHelper; -import fr.gaulupeau.apps.Poche.data.Settings; -import fr.gaulupeau.apps.Poche.data.StorageHelper; -import fr.gaulupeau.apps.Poche.data.dao.entities.QueueItem; -import fr.gaulupeau.apps.Poche.events.ArticlesChangedEvent; -import fr.gaulupeau.apps.Poche.events.EventHelper; -import fr.gaulupeau.apps.Poche.events.FeedsChangedEvent; -import fr.gaulupeau.apps.Poche.network.ClientCredentials; -import fr.gaulupeau.apps.Poche.network.WallabagConnection; -import fr.gaulupeau.apps.Poche.network.WallabagWebService; -import fr.gaulupeau.apps.Poche.network.tasks.TestApiAccessTask; -import fr.gaulupeau.apps.Poche.service.AlarmHelper; -import fr.gaulupeau.apps.Poche.service.OperationsHelper; import fr.gaulupeau.apps.Poche.ui.BaseActionBarActivity; -import fr.gaulupeau.apps.Poche.ui.Themes; -import fr.gaulupeau.apps.Poche.utils.LoggingUtils; public class SettingsActivity extends BaseActionBarActivity { @@ -52,726 +13,17 @@ public class SettingsActivity extends BaseActionBarActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + setContentView(R.layout.activity_settings); + + Toolbar toolbar = findViewById(R.id.toolbar); + setSupportActionBar(toolbar); + toolbar.setNavigationOnClickListener(v -> onBackPressed()); + if(savedInstanceState == null) { getFragmentManager().beginTransaction() - .replace(android.R.id.content, new SettingsFragment()) + .replace(R.id.content, new SettingsFragment()) .commit(); } } - public static class SettingsFragment extends PreferenceFragment - implements Preference.OnPreferenceClickListener, - Preference.OnPreferenceChangeListener, - SharedPreferences.OnSharedPreferenceChangeListener, - ConfigurationTestHelper.ResultHandler, - ConfigurationTestHelper.GetCredentialsHandler { - - private static final String TAG = SettingsFragment.class.getSimpleName(); - - private static final int[] SUMMARIES_TO_INITIATE = { - R.string.pref_key_connection_url, - R.string.pref_key_connection_advanced_httpAuthUsername, - R.string.pref_key_connection_advanced_httpAuthPassword, - R.string.pref_key_connection_username, - R.string.pref_key_connection_password, - R.string.pref_key_connection_api_clientID, - R.string.pref_key_connection_api_clientSecret, - R.string.pref_key_ui_theme, - R.string.pref_key_ui_article_fontSize, - R.string.pref_key_ui_screenScrolling_percent, - R.string.pref_key_autoSync_interval, - R.string.pref_key_autoSync_type, - R.string.pref_key_storage_dbPath - }; - - private Settings settings; - - private boolean autoSyncChanged; - private boolean oldAutoSyncEnabled; - private long oldAutoSyncInterval; - - private boolean autoSyncQueueChanged; - private boolean oldAutoSyncQueueEnabled; - - private boolean checkUserChanged; - private String oldUrl; - private String oldHttpAuthUsername; - private String oldUsername; - private String oldApiClientID; - - private boolean invalidateConfiguration; - private boolean serviceWrapperReinitializationNeeded; - - private boolean imageCachingChanged; - private boolean oldImageCacheEnabled; - - private boolean readingSpeedChanged; - private int oldReadingSpeed; - - private boolean keepScreenOnChanged; - private boolean oldkeepScreenOn; - - private ConfigurationTestHelper configurationTestHelper; - - public SettingsFragment() {} - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - addPreferencesFromResource(R.xml.preferences); - - settings = App.getSettings(); - - setOnClickListener(R.string.pref_key_connection_wizard); - setOnClickListener(R.string.pref_key_connection_autofill); - setOnClickListener(R.string.pref_key_sync_syncTypes_description); - setOnClickListener(R.string.pref_key_ui_disableTouch_keyCode); - setOnClickListener(R.string.pref_key_misc_wipeDB); - setOnClickListener(R.string.pref_key_misc_localQueue_dumpToFile); - setOnClickListener(R.string.pref_key_misc_localQueue_removeFirstItem); - setOnClickListener(R.string.pref_key_misc_logging_logcatToFile); - - ListPreference themeListPreference = (ListPreference)findPreference( - getString(R.string.pref_key_ui_theme)); - if(themeListPreference != null) { - Themes.Theme[] themes = Themes.Theme.values(); - String[] themeEntries = new String[themes.length]; - String[] themeEntryValues = new String[themes.length]; - for(int i = 0; i < themes.length; i++) { - themeEntries[i] = getString(themes[i].getNameId()); - themeEntryValues[i] = themes[i].toString(); - } - - themeListPreference.setEntries(themeEntries); - themeListPreference.setEntryValues(themeEntryValues); - } - - ListPreference autoSyncIntervalListPreference = (ListPreference)findPreference( - getString(R.string.pref_key_autoSync_interval)); - if(autoSyncIntervalListPreference != null) { - // may set arbitrary values on Android API 19+ - autoSyncIntervalListPreference.setEntries(new String[] { - getString(R.string.pref_option_autoSync_interval_15m), - getString(R.string.pref_option_autoSync_interval_30m), - getString(R.string.pref_option_autoSync_interval_1h), - getString(R.string.pref_option_autoSync_interval_12h), - getString(R.string.pref_option_autoSync_interval_24h) - }); - autoSyncIntervalListPreference.setEntryValues(new String[] { - String.valueOf(AlarmManager.INTERVAL_FIFTEEN_MINUTES), - String.valueOf(AlarmManager.INTERVAL_HALF_HOUR), - String.valueOf(AlarmManager.INTERVAL_HOUR), - String.valueOf(AlarmManager.INTERVAL_HALF_DAY), - String.valueOf(AlarmManager.INTERVAL_DAY) - }); - } - - CheckBoxPreference handleHttpSchemePreference = (CheckBoxPreference) findPreference( - getString(R.string.pref_key_misc_handleHttpScheme)); - if (handleHttpSchemePreference != null) { - handleHttpSchemePreference.setChecked(settings.isHandlingHttpScheme()); - handleHttpSchemePreference.setOnPreferenceChangeListener(this); - } - - ListPreference dbPathListPreference = (ListPreference)findPreference( - getString(R.string.pref_key_storage_dbPath)); - if(dbPathListPreference != null) { - List entriesList = new ArrayList<>(2); - List entryValuesList = new ArrayList<>(2); - - entriesList.add(getString(R.string.pref_name_storage_dbPath_internalStorage)); - entryValuesList.add(""); - - if(StorageHelper.isExternalStorageWritable()) { - entriesList.add(getString(R.string.pref_name_storage_dbPath_externalStorage)); - entryValuesList.add(StorageHelper.getExternalStoragePath()); - } - - dbPathListPreference.setEntries(entriesList.toArray(new String[0])); - dbPathListPreference.setEntryValues(entryValuesList.toArray(new String[0])); - - dbPathListPreference.setOnPreferenceChangeListener(this); - } - - for(int keyID: SUMMARIES_TO_INITIATE) { - updateSummary(keyID); - } - } - - @Override - public void onStart() { - super.onStart(); - - Log.d(TAG, "onStart() started"); - - resetChanges(); - - settings.getSharedPreferences().registerOnSharedPreferenceChangeListener(this); - } - - @Override - public void onStop() { - Log.d(TAG, "onStop() started"); - - settings.getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this); - - if(configurationTestHelper != null) { - configurationTestHelper.cancel(); - configurationTestHelper = null; - } - - applyChanges(); - - super.onStop(); - } - - private void resetChanges() { - Log.d(TAG, "resetChanges() started"); - - autoSyncChanged = false; - oldAutoSyncEnabled = settings.isAutoSyncEnabled(); - oldAutoSyncInterval = settings.getAutoSyncInterval(); - - autoSyncQueueChanged = false; - oldAutoSyncQueueEnabled = settings.isAutoSyncQueueEnabled(); - - checkUserChanged = false; - oldUrl = settings.getUrl(); - oldHttpAuthUsername = settings.getHttpAuthUsername(); - oldUsername = settings.getUsername(); - oldApiClientID = settings.getApiClientID(); - - imageCachingChanged = false; - oldImageCacheEnabled = settings.isImageCacheEnabled(); - - readingSpeedChanged = false; - oldReadingSpeed = settings.getReadingSpeed(); - - keepScreenOnChanged = false; - oldkeepScreenOn = settings.isKeepScreenOn(); - } - - private void applyChanges() { - Log.d(TAG, "applyChanges() started"); - - if(autoSyncChanged) { - autoSyncChanged = false; - Log.d(TAG, "applyChanges() autoSyncChanged is true"); - - boolean newAutoSyncEnabled = settings.isAutoSyncEnabled(); - long newAutoSyncInterval = settings.getAutoSyncInterval(); - if(newAutoSyncEnabled != oldAutoSyncEnabled) { - if(newAutoSyncEnabled) { - AlarmHelper.setAlarm(getActivity(), newAutoSyncInterval, true); - } else { - AlarmHelper.unsetAlarm(getActivity(), true); - } - } else if(newAutoSyncEnabled) { - if(newAutoSyncInterval != oldAutoSyncInterval) { - AlarmHelper.updateAlarmInterval(getActivity(), newAutoSyncInterval); - } - } - } - - if(autoSyncQueueChanged) { - autoSyncQueueChanged = false; - Log.d(TAG, "applyChanges() autoSyncQueueChanged is true"); - - boolean newAutoSyncQueueEnabled = settings.isAutoSyncQueueEnabled(); - if(newAutoSyncQueueEnabled != oldAutoSyncQueueEnabled) { - if(newAutoSyncQueueEnabled) { - if(settings.isOfflineQueuePending()) { - Settings.enableConnectivityChangeReceiver(getActivity(), true); - } - } else { - Settings.enableConnectivityChangeReceiver(getActivity(), false); - } - } - } - - if(checkUserChanged) { - checkUserChanged = false; - - boolean userChanged = false; - if(!TextUtils.equals(settings.getUrl(), oldUrl) - || !TextUtils.equals(settings.getUsername(), oldUsername) - || !TextUtils.equals(settings.getApiClientID(), oldApiClientID)) { - userChanged = true; - } else if(!TextUtils.equals(settings.getHttpAuthUsername(), oldHttpAuthUsername) - && (settings.getUsername() == null || settings.getUsername().isEmpty())) { - userChanged = true; - } - - if(userChanged) { - settings.setApiRefreshToken(""); - settings.setApiAccessToken(""); - - OperationsHelper.wipeDB(settings); - } - } - - if(invalidateConfiguration) { - invalidateConfiguration = false; - - Log.i(TAG, "applyChanges() setting isConfigurationOk(false)"); - settings.setConfigurationOk(false); - } - - if(serviceWrapperReinitializationNeeded) { - serviceWrapperReinitializationNeeded = false; - - Log.i(TAG, "applyChanges() calling WallabagConnection.resetWallabagService()"); - WallabagConnection.resetWallabagService(); - } - - if(imageCachingChanged) { - imageCachingChanged = false; - - if(!oldImageCacheEnabled && settings.isImageCacheEnabled() - && settings.isFirstSyncDone()) { - Log.i(TAG, "applyChanges() image caching changed, starting image fetching"); - OperationsHelper.fetchImages(App.getInstance()); - } - } - - if(readingSpeedChanged) { - readingSpeedChanged = false; - - if(oldReadingSpeed != settings.getReadingSpeed()) { - Log.i(TAG, "applyChanges() reading speed changed, posting event"); - - ArticlesChangedEvent event = new ArticlesChangedEvent(); - event.invalidateAll(FeedsChangedEvent.ChangeType.ESTIMATED_READING_TIME_CHANGED); - EventHelper.postEvent(event); - } - } - - if(keepScreenOnChanged) { - keepScreenOnChanged = false; - - if(!oldkeepScreenOn) { - Log.i(TAG, "applyChanges() keep screen on changed, keep screen on"); - } - } - } - - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - Log.d(TAG, String.format("onPreferenceChange(key: %s, newValue: %s)", - preference.getKey(), newValue)); - - int keyID = PreferenceKeysMap.getInstance().getPrefKeyId(preference); - switch(keyID) { - case R.string.pref_key_misc_handleHttpScheme: - settings.setHandleHttpScheme((Boolean)newValue); - break; - - case R.string.pref_key_storage_dbPath: - if(TextUtils.equals(settings.getDbPath(), (String)newValue)) { - Log.d(TAG, "onPreferenceChange() new DbPath is the same"); - } else if(settings.moveDb((String)newValue)) { // TODO: do in a background thread - DbConnection.resetSession(); - - Toast.makeText(getActivity(), R.string.pref_name_storage_dbPath_dbMoved, - Toast.LENGTH_SHORT).show(); - } else { - Log.e(TAG, "onPreferenceChange() couldn't move DB; ignoring preference change"); - return false; - } - break; - } - - return true; - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - Log.d(TAG, "onSharedPreferenceChanged(" + key + ")"); - - boolean themeChanged = false; - - int keyResID = PreferenceKeysMap.getInstance().getPrefKeyIdByStringKey(key); - switch(keyResID) { - case R.string.pref_key_ui_theme: - themeChanged = true; - break; - - case R.string.pref_key_autoSync_enabled: - autoSyncChanged = true; - break; - - case R.string.pref_key_autoSync_interval: - autoSyncChanged = true; - break; - - case R.string.pref_key_autoSyncQueue_enabled: - autoSyncQueueChanged = true; - break; - - case R.string.pref_key_connection_url: - Log.d(TAG, "onSharedPreferenceChanged() serviceWrapperReinitializationNeeded"); - serviceWrapperReinitializationNeeded = true; - case R.string.pref_key_connection_advanced_httpAuthUsername: - case R.string.pref_key_connection_advanced_httpAuthPassword: - case R.string.pref_key_connection_username: - case R.string.pref_key_connection_password: - case R.string.pref_key_connection_api_clientID: - case R.string.pref_key_connection_api_clientSecret: - Log.i(TAG, "onSharedPreferenceChanged() invalidateConfiguration"); - invalidateConfiguration = true; - break; - - case R.string.pref_key_imageCache_enabled: - imageCachingChanged = true; - break; - - case R.string.pref_key_ui_readingSpeed: - readingSpeedChanged = true; - break; - - case R.string.pref_key_ui_keepScreenOn: - keepScreenOnChanged = true; - break; - } - - switch(keyResID) { - case R.string.pref_key_connection_url: - case R.string.pref_key_connection_advanced_httpAuthUsername: - case R.string.pref_key_connection_username: - case R.string.pref_key_connection_api_clientID: - checkUserChanged = true; - break; - } - - // not optimal :/ - updateSummary(keyResID); - - if(themeChanged) { - Log.d(TAG, "onSharedPreferenceChanged() theme changed"); - - Themes.init(); - - Activity activity = getActivity(); - if(activity != null) Themes.checkTheme(activity); - } - } - - @Override - public boolean onPreferenceClick(Preference preference) { - switch (PreferenceKeysMap.getInstance().getPrefKeyId(preference)) { - case R.string.pref_key_connection_wizard: { - Activity activity = getActivity(); - if(activity != null) { - ConnectionWizardActivity.runWizard(activity, true); - - activity.finish(); - } - - return true; - } - case R.string.pref_key_connection_autofill: { - configurationTestHelper = new ConfigurationTestHelper( - getActivity(), this, this, settings, false); - configurationTestHelper.test(); - - return true; - } - case R.string.pref_key_sync_syncTypes_description: { - Activity activity = getActivity(); - if(activity != null) { - new AlertDialog.Builder(activity) - .setTitle(R.string.pref_name_sync_syncTypes) - .setMessage(R.string.pref_desc_sync_syncTypes_text) - .setPositiveButton(R.string.ok, null) - .show(); - } - return true; - } - case R.string.pref_key_ui_disableTouch_keyCode: { - showDisableTouchSetKeyCodeDialog(); - return true; - } - case R.string.pref_key_misc_wipeDB: { - Activity activity = getActivity(); - if(activity != null) { - new AlertDialog.Builder(activity) - .setTitle(R.string.pref_name_misc_wipeDB_confirmTitle) - .setMessage(R.string.pref_name_misc_wipeDB_confirmMessage) - .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - OperationsHelper.wipeDB(App.getSettings()); - } - }) - .setNegativeButton(R.string.negative_answer, null) - .show(); - } - return true; - } - case R.string.pref_key_misc_localQueue_dumpToFile: { - dumpOfflineQueue(); - return true; - } - case R.string.pref_key_misc_localQueue_removeFirstItem: { - removeFirstOfflineQueueItem(); - return true; - } - case R.string.pref_key_misc_logging_logcatToFile: { - Activity activity = getActivity(); - if (activity != null) { - LoggingUtils.saveLogcatToFile(activity); - } - return true; - } - } - - return false; - } - - private void dumpOfflineQueue() { - Activity activity = getActivity(); - if (activity == null) return; - - QueueHelper queueHelper = new QueueHelper(DbConnection.getSession()); - List items = queueHelper.getQueueItems(); - if (items.isEmpty()) { - Toast.makeText(activity, R.string.misc_localQueue_empty, Toast.LENGTH_SHORT).show(); - return; - } - - String string = getString(R.string.misc_localQueue_dumpToFile_header, - getString(R.string.issues_url)) + "\r\n\r\n" - + queueItemsToString(items); - - try { - File file = StorageHelper.dumpQueueData(string); - Toast.makeText(activity, getString(R.string.misc_localQueue_dumpToFile_result_dumped, - file.getAbsolutePath()), Toast.LENGTH_LONG).show(); - } catch (Exception e) { - Log.w(TAG, "Error during dumping offline queue", e); - Toast.makeText(activity, getString(R.string.misc_localQueue_dumpToFile_result_error, - e.toString()), Toast.LENGTH_LONG).show(); - } - } - - private String queueItemsToString(List items) { - String nl = "\r\n"; - String delim = "; "; - - StringBuilder sb = new StringBuilder(); - - sb.append("id").append(delim) - .append("action").append(delim) - .append("articleId").append(delim) - .append("localArticleId").append(delim) - .append("extra").append(delim) - .append("extra2").append(nl); - - for (QueueItem item : items) { - sb.append(item.getId()).append(delim) - .append(item.getAction()).append(delim) - .append(item.getArticleId()).append(delim) - .append(item.getLocalArticleId()).append(delim) - .append(item.getExtra()).append(delim) - .append(item.getExtra2()).append(nl); - } - - return sb.toString(); - } - - private void removeFirstOfflineQueueItem() { - Activity activity = getActivity(); - if (activity == null) return; - - QueueHelper queueHelper = new QueueHelper(DbConnection.getSession()); - List items = queueHelper.getQueueItems(); - if (items.isEmpty()) { - Toast.makeText(activity, R.string.misc_localQueue_empty, Toast.LENGTH_SHORT).show(); - return; - } - - queueHelper.dequeueItems(Collections.singletonList(items.get(0))); - Toast.makeText(activity, R.string.misc_localQueue_removeFirstItem_done, Toast.LENGTH_SHORT).show(); - } - - private void showDisableTouchSetKeyCodeDialog() { - Activity activity = getActivity(); - if(activity != null) { - AlertDialog.Builder builder = new AlertDialog.Builder(activity); - builder.setTitle(R.string.d_disableTouch_changeKey_title); - - @SuppressLint("InflateParams") - final View view = activity.getLayoutInflater().inflate(R.layout.dialog_set_key, null); - final TextView keyCodeTextView = (TextView)view.findViewById(R.id.tv_keyCode); - - setIntToTextView(keyCodeTextView, settings.getDisableTouchKeyCode()); - - builder.setView(view); - - DialogInterface.OnKeyListener keyListener = new DialogInterface.OnKeyListener() { - @Override - public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { - if (event.getAction() != KeyEvent.ACTION_DOWN) return false; - - setIntToTextView(keyCodeTextView, keyCode); - - return false; - } - }; - - builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - try { - settings.setDisableTouchKeyCode(Integer.parseInt( - keyCodeTextView.getText().toString())); - } catch(NumberFormatException ignored) {} - } - }); - builder.setNegativeButton(android.R.string.cancel, null); - builder.setOnKeyListener(keyListener); - - builder.show(); - } - } - - @SuppressLint("SetTextI18n") - private void setIntToTextView(TextView textView, int value) { - textView.setText(Integer.toString(value)); - } - - @Override - public void onGetCredentialsResult(ClientCredentials clientCredentials) { - setTextPreference(R.string.pref_key_connection_api_clientID, - clientCredentials.clientID); - setTextPreference(R.string.pref_key_connection_api_clientSecret, - clientCredentials.clientSecret); - } - - @Override - public void onGetCredentialsFail() {} - - @Override - public void onConfigurationTestSuccess(String url) { - Log.d(TAG, String.format("onConfigurationTestSuccess(%s)", url)); - - if(url != null) { - setTextPreference(R.string.pref_key_connection_url, url); - } - - settings.setConfigurationOk(true); - settings.setConfigurationErrorShown(false); - - invalidateConfiguration = false; - - Toast.makeText(getActivity(), R.string.settings_parametersAutofilled, - Toast.LENGTH_SHORT).show(); - } - - @Override - public void onConnectionTestFail(WallabagWebService.ConnectionTestResult result, - String details) {} - - @Override - public void onApiAccessTestFail(TestApiAccessTask.Result result, String details) {} - - private void setOnClickListener(int keyResID) { - Preference preference = findPreference(getString(keyResID)); - if(preference != null) { - preference.setOnPreferenceClickListener(this); - } - } - - private void setTextPreference(int preferenceID, String value) { - EditTextPreference preference = (EditTextPreference) - findPreference(getString(preferenceID)); - - if(preference != null) { - preference.setText(value); - } - } - - private void updateSummary(int keyResID) { - String key = getString(keyResID); - - switch(keyResID) { - case R.string.pref_key_connection_url: - EditTextPreference preference = (EditTextPreference) - findPreference(key); - if(preference != null) { - String value = preference.getText(); - setSummary(key, (value == null || value.isEmpty()) - ? getString(R.string.pref_desc_connection_url) : value); - } - break; - - case R.string.pref_key_connection_username: - case R.string.pref_key_connection_api_clientID: - case R.string.pref_key_connection_advanced_httpAuthUsername: - case R.string.pref_key_ui_article_fontSize: - case R.string.pref_key_ui_screenScrolling_percent: - setEditTextSummaryFromContent(key); - break; - - case R.string.pref_key_ui_theme: - case R.string.pref_key_autoSync_interval: - case R.string.pref_key_autoSync_type: - setListSummaryFromContent(key); - break; - - case R.string.pref_key_connection_password: - case R.string.pref_key_connection_api_clientSecret: - case R.string.pref_key_connection_advanced_httpAuthPassword: - setPasswordSummary(key); - break; - - case R.string.pref_key_storage_dbPath: - ListPreference dbPathListPreference = (ListPreference)findPreference( - getString(R.string.pref_key_storage_dbPath)); - if(dbPathListPreference != null) { - CharSequence value = dbPathListPreference.getEntry(); - if(TextUtils.isEmpty(value)) { - dbPathListPreference.setSummary(R.string.pref_name_storage_dbPath_internalStorage); - } else if(value.equals(StorageHelper.getExternalStoragePath())) { - dbPathListPreference.setSummary(R.string.pref_name_storage_dbPath_externalStorage); - } else { - dbPathListPreference.setSummary(value); - } - } - break; - } - } - - private void setSummary(String key, String text) { - Preference preference = findPreference(key); - if(preference != null) { - preference.setSummary(text); - } - } - - private void setEditTextSummaryFromContent(String key) { - EditTextPreference preference = (EditTextPreference)findPreference(key); - if(preference != null) { - preference.setSummary(preference.getText()); - } - } - - private void setListSummaryFromContent(String key) { - ListPreference preference = (ListPreference)findPreference(key); - if(preference != null) { - preference.setSummary(preference.getEntry()); - } - } - - private void setPasswordSummary(String key) { - EditTextPreference preference = (EditTextPreference)findPreference(key); - if(preference != null) { - String value = preference.getText(); - preference.setSummary(value == null || value.isEmpty() ? "" : "********"); - } - } - - } - } diff --git a/app/src/main/java/fr/gaulupeau/apps/Poche/ui/preferences/SettingsFragment.java b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/preferences/SettingsFragment.java new file mode 100644 index 000000000..ed5a54160 --- /dev/null +++ b/app/src/main/java/fr/gaulupeau/apps/Poche/ui/preferences/SettingsFragment.java @@ -0,0 +1,765 @@ +package fr.gaulupeau.apps.Poche.ui.preferences; + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.AlarmManager; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.preference.CheckBoxPreference; +import android.preference.EditTextPreference; +import android.preference.ListPreference; +import android.preference.Preference; +import android.preference.PreferenceFragment; +import android.text.TextUtils; +import android.util.Log; +import android.view.KeyEvent; +import android.view.View; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.appcompat.app.AlertDialog; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import fr.gaulupeau.apps.InThePoche.R; +import fr.gaulupeau.apps.Poche.App; +import fr.gaulupeau.apps.Poche.data.DbConnection; +import fr.gaulupeau.apps.Poche.data.PreferenceKeysMap; +import fr.gaulupeau.apps.Poche.data.QueueHelper; +import fr.gaulupeau.apps.Poche.data.Settings; +import fr.gaulupeau.apps.Poche.data.StorageHelper; +import fr.gaulupeau.apps.Poche.data.dao.entities.QueueItem; +import fr.gaulupeau.apps.Poche.events.ArticlesChangedEvent; +import fr.gaulupeau.apps.Poche.events.EventHelper; +import fr.gaulupeau.apps.Poche.events.FeedsChangedEvent; +import fr.gaulupeau.apps.Poche.network.ClientCredentials; +import fr.gaulupeau.apps.Poche.network.WallabagConnection; +import fr.gaulupeau.apps.Poche.network.WallabagWebService; +import fr.gaulupeau.apps.Poche.network.tasks.TestApiAccessTask; +import fr.gaulupeau.apps.Poche.service.AlarmHelper; +import fr.gaulupeau.apps.Poche.service.OperationsHelper; +import fr.gaulupeau.apps.Poche.ui.Themes; +import fr.gaulupeau.apps.Poche.utils.LoggingUtils; + +public class SettingsFragment extends PreferenceFragment + implements Preference.OnPreferenceClickListener, + Preference.OnPreferenceChangeListener, + SharedPreferences.OnSharedPreferenceChangeListener, + ConfigurationTestHelper.ResultHandler, + ConfigurationTestHelper.GetCredentialsHandler { + + protected final String TAG = SettingsFragment.class.getSimpleName(); + + private static final int[] SUMMARIES_TO_INITIATE = { + R.string.pref_key_connection_url, + R.string.pref_key_connection_advanced_httpAuthUsername, + R.string.pref_key_connection_advanced_httpAuthPassword, + R.string.pref_key_connection_username, + R.string.pref_key_connection_password, + R.string.pref_key_connection_api_clientID, + R.string.pref_key_connection_api_clientSecret, + R.string.pref_key_ui_theme, + R.string.pref_key_ui_article_fontSize, + R.string.pref_key_ui_screenScrolling_percent, + R.string.pref_key_autoSync_interval, + R.string.pref_key_autoSync_type, + R.string.pref_key_storage_dbPath + }; + + private Settings settings; + + private boolean autoSyncChanged; + private boolean oldAutoSyncEnabled; + private long oldAutoSyncInterval; + + private boolean autoSyncQueueChanged; + private boolean oldAutoSyncQueueEnabled; + + private boolean checkUserChanged; + private String oldUrl; + private String oldHttpAuthUsername; + private String oldUsername; + private String oldApiClientID; + + private boolean invalidateConfiguration; + private boolean serviceWrapperReinitializationNeeded; + + private boolean imageCachingChanged; + private boolean oldImageCacheEnabled; + + private boolean readingSpeedChanged; + private int oldReadingSpeed; + + private boolean keepScreenOnChanged; + private boolean oldkeepScreenOn; + + private ConfigurationTestHelper configurationTestHelper; + + public SettingsFragment() { + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + addPreferencesFromResource(R.xml.preferences); + + settings = App.getSettings(); + + setOnClickListener(R.string.pref_key_connection_wizard); + setOnClickListener(R.string.pref_key_connection_autofill); + setOnClickListener(R.string.pref_key_sync_syncTypes_description); + setOnClickListener(R.string.pref_key_ui_disableTouch_keyCode); + setOnClickListener(R.string.pref_key_misc_wipeDB); + setOnClickListener(R.string.pref_key_misc_localQueue_dumpToFile); + setOnClickListener(R.string.pref_key_misc_localQueue_removeFirstItem); + setOnClickListener(R.string.pref_key_misc_logging_logcatToFile); + + ListPreference themeListPreference = (ListPreference) findPreference( + getString(R.string.pref_key_ui_theme)); + if (themeListPreference != null) { + Themes.Theme[] themes = Themes.Theme.values(); + String[] themeEntries = new String[themes.length]; + String[] themeEntryValues = new String[themes.length]; + for (int i = 0; i < themes.length; i++) { + themeEntries[i] = getString(themes[i].getNameId()); + themeEntryValues[i] = themes[i].toString(); + } + + themeListPreference.setEntries(themeEntries); + themeListPreference.setEntryValues(themeEntryValues); + } + + ListPreference autoSyncIntervalListPreference = (ListPreference) findPreference( + getString(R.string.pref_key_autoSync_interval)); + if (autoSyncIntervalListPreference != null) { + // may set arbitrary values on Android API 19+ + autoSyncIntervalListPreference.setEntries(new String[]{ + getString(R.string.pref_option_autoSync_interval_15m), + getString(R.string.pref_option_autoSync_interval_30m), + getString(R.string.pref_option_autoSync_interval_1h), + getString(R.string.pref_option_autoSync_interval_12h), + getString(R.string.pref_option_autoSync_interval_24h) + }); + autoSyncIntervalListPreference.setEntryValues(new String[]{ + String.valueOf(AlarmManager.INTERVAL_FIFTEEN_MINUTES), + String.valueOf(AlarmManager.INTERVAL_HALF_HOUR), + String.valueOf(AlarmManager.INTERVAL_HOUR), + String.valueOf(AlarmManager.INTERVAL_HALF_DAY), + String.valueOf(AlarmManager.INTERVAL_DAY) + }); + } + + CheckBoxPreference handleHttpSchemePreference = (CheckBoxPreference) findPreference( + getString(R.string.pref_key_misc_handleHttpScheme)); + if (handleHttpSchemePreference != null) { + handleHttpSchemePreference.setChecked(settings.isHandlingHttpScheme()); + handleHttpSchemePreference.setOnPreferenceChangeListener(this); + } + + ListPreference dbPathListPreference = (ListPreference) findPreference( + getString(R.string.pref_key_storage_dbPath)); + if (dbPathListPreference != null) { + List entriesList = new ArrayList<>(2); + List entryValuesList = new ArrayList<>(2); + + entriesList.add(getString(R.string.pref_name_storage_dbPath_internalStorage)); + entryValuesList.add(""); + + if (StorageHelper.isExternalStorageWritable()) { + entriesList.add(getString(R.string.pref_name_storage_dbPath_externalStorage)); + entryValuesList.add(StorageHelper.getExternalStoragePath()); + } + + dbPathListPreference.setEntries(entriesList.toArray(new String[0])); + dbPathListPreference.setEntryValues(entryValuesList.toArray(new String[0])); + + dbPathListPreference.setOnPreferenceChangeListener(this); + } + + for (int keyID : SUMMARIES_TO_INITIATE) { + updateSummary(keyID); + } + } + + @Override + public void onStart() { + super.onStart(); + + Log.d(TAG, "onStart() started"); + + resetChanges(); + + settings.getSharedPreferences().registerOnSharedPreferenceChangeListener(this); + } + + @Override + public void onStop() { + Log.d(TAG, "onStop() started"); + + settings.getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this); + + if (configurationTestHelper != null) { + configurationTestHelper.cancel(); + configurationTestHelper = null; + } + + applyChanges(); + + super.onStop(); + } + + private void resetChanges() { + Log.d(TAG, "resetChanges() started"); + + autoSyncChanged = false; + oldAutoSyncEnabled = settings.isAutoSyncEnabled(); + oldAutoSyncInterval = settings.getAutoSyncInterval(); + + autoSyncQueueChanged = false; + oldAutoSyncQueueEnabled = settings.isAutoSyncQueueEnabled(); + + checkUserChanged = false; + oldUrl = settings.getUrl(); + oldHttpAuthUsername = settings.getHttpAuthUsername(); + oldUsername = settings.getUsername(); + oldApiClientID = settings.getApiClientID(); + + imageCachingChanged = false; + oldImageCacheEnabled = settings.isImageCacheEnabled(); + + readingSpeedChanged = false; + oldReadingSpeed = settings.getReadingSpeed(); + + keepScreenOnChanged = false; + oldkeepScreenOn = settings.isKeepScreenOn(); + } + + private void applyChanges() { + Log.d(TAG, "applyChanges() started"); + + if (autoSyncChanged) { + autoSyncChanged = false; + Log.d(TAG, "applyChanges() autoSyncChanged is true"); + + boolean newAutoSyncEnabled = settings.isAutoSyncEnabled(); + long newAutoSyncInterval = settings.getAutoSyncInterval(); + if (newAutoSyncEnabled != oldAutoSyncEnabled) { + if (newAutoSyncEnabled) { + AlarmHelper.setAlarm(getActivity(), newAutoSyncInterval, true); + } else { + AlarmHelper.unsetAlarm(getActivity(), true); + } + } else if (newAutoSyncEnabled) { + if (newAutoSyncInterval != oldAutoSyncInterval) { + AlarmHelper.updateAlarmInterval(getActivity(), newAutoSyncInterval); + } + } + } + + if (autoSyncQueueChanged) { + autoSyncQueueChanged = false; + Log.d(TAG, "applyChanges() autoSyncQueueChanged is true"); + + boolean newAutoSyncQueueEnabled = settings.isAutoSyncQueueEnabled(); + if (newAutoSyncQueueEnabled != oldAutoSyncQueueEnabled) { + if (newAutoSyncQueueEnabled) { + if (settings.isOfflineQueuePending()) { + Settings.enableConnectivityChangeReceiver(getActivity(), true); + } + } else { + Settings.enableConnectivityChangeReceiver(getActivity(), false); + } + } + } + + if (checkUserChanged) { + checkUserChanged = false; + + boolean userChanged = false; + if (!TextUtils.equals(settings.getUrl(), oldUrl) + || !TextUtils.equals(settings.getUsername(), oldUsername) + || !TextUtils.equals(settings.getApiClientID(), oldApiClientID)) { + userChanged = true; + } else if (!TextUtils.equals(settings.getHttpAuthUsername(), oldHttpAuthUsername) + && (settings.getUsername() == null || settings.getUsername().isEmpty())) { + userChanged = true; + } + + if (userChanged) { + settings.setApiRefreshToken(""); + settings.setApiAccessToken(""); + + OperationsHelper.wipeDB(settings); + } + } + + if (invalidateConfiguration) { + invalidateConfiguration = false; + + Log.i(TAG, "applyChanges() setting isConfigurationOk(false)"); + settings.setConfigurationOk(false); + } + + if (serviceWrapperReinitializationNeeded) { + serviceWrapperReinitializationNeeded = false; + + Log.i(TAG, "applyChanges() calling WallabagConnection.resetWallabagService()"); + WallabagConnection.resetWallabagService(); + } + + if (imageCachingChanged) { + imageCachingChanged = false; + + if (!oldImageCacheEnabled && settings.isImageCacheEnabled() + && settings.isFirstSyncDone()) { + Log.i(TAG, "applyChanges() image caching changed, starting image fetching"); + OperationsHelper.fetchImages(App.getInstance()); + } + } + + if (readingSpeedChanged) { + readingSpeedChanged = false; + + if (oldReadingSpeed != settings.getReadingSpeed()) { + Log.i(TAG, "applyChanges() reading speed changed, posting event"); + + ArticlesChangedEvent event = new ArticlesChangedEvent(); + event.invalidateAll(FeedsChangedEvent.ChangeType.ESTIMATED_READING_TIME_CHANGED); + EventHelper.postEvent(event); + } + } + + if (keepScreenOnChanged) { + keepScreenOnChanged = false; + + if (!oldkeepScreenOn) { + Log.i(TAG, "applyChanges() keep screen on changed, keep screen on"); + } + } + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + Log.d(TAG, String.format("onPreferenceChange(key: %s, newValue: %s)", + preference.getKey(), newValue)); + + int keyID = PreferenceKeysMap.getInstance().getPrefKeyId(preference); + switch (keyID) { + case R.string.pref_key_misc_handleHttpScheme: + settings.setHandleHttpScheme((Boolean) newValue); + break; + + case R.string.pref_key_storage_dbPath: + if (TextUtils.equals(settings.getDbPath(), (String) newValue)) { + Log.d(TAG, "onPreferenceChange() new DbPath is the same"); + } else if (settings.moveDb((String) newValue)) { // TODO: do in a background thread + DbConnection.resetSession(); + + Toast.makeText(getActivity(), R.string.pref_name_storage_dbPath_dbMoved, + Toast.LENGTH_SHORT).show(); + } else { + Log.e(TAG, "onPreferenceChange() couldn't move DB; ignoring preference change"); + return false; + } + break; + } + + return true; + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + Log.d(TAG, "onSharedPreferenceChanged(" + key + ")"); + + boolean themeChanged = false; + + int keyResID = PreferenceKeysMap.getInstance().getPrefKeyIdByStringKey(key); + switch (keyResID) { + case R.string.pref_key_ui_theme: + themeChanged = true; + break; + + case R.string.pref_key_autoSync_enabled: + autoSyncChanged = true; + break; + + case R.string.pref_key_autoSync_interval: + autoSyncChanged = true; + break; + + case R.string.pref_key_autoSyncQueue_enabled: + autoSyncQueueChanged = true; + break; + + case R.string.pref_key_connection_url: + Log.d(TAG, "onSharedPreferenceChanged() serviceWrapperReinitializationNeeded"); + serviceWrapperReinitializationNeeded = true; + case R.string.pref_key_connection_advanced_httpAuthUsername: + case R.string.pref_key_connection_advanced_httpAuthPassword: + case R.string.pref_key_connection_username: + case R.string.pref_key_connection_password: + case R.string.pref_key_connection_api_clientID: + case R.string.pref_key_connection_api_clientSecret: + Log.i(TAG, "onSharedPreferenceChanged() invalidateConfiguration"); + invalidateConfiguration = true; + break; + + case R.string.pref_key_imageCache_enabled: + imageCachingChanged = true; + break; + + case R.string.pref_key_ui_readingSpeed: + readingSpeedChanged = true; + break; + + case R.string.pref_key_ui_keepScreenOn: + keepScreenOnChanged = true; + break; + } + + switch (keyResID) { + case R.string.pref_key_connection_url: + case R.string.pref_key_connection_advanced_httpAuthUsername: + case R.string.pref_key_connection_username: + case R.string.pref_key_connection_api_clientID: + checkUserChanged = true; + break; + } + + // not optimal :/ + updateSummary(keyResID); + + if (themeChanged) { + Log.d(TAG, "onSharedPreferenceChanged() theme changed"); + + Themes.init(); + + Activity activity = getActivity(); + if (activity != null) Themes.checkTheme(activity); + } + } + + @Override + public boolean onPreferenceClick(Preference preference) { + switch (PreferenceKeysMap.getInstance().getPrefKeyId(preference)) { + case R.string.pref_key_connection_wizard: { + Activity activity = getActivity(); + if (activity != null) { + ConnectionWizardActivity.runWizard(activity, true); + + activity.finish(); + } + + return true; + } + case R.string.pref_key_connection_autofill: { + configurationTestHelper = new ConfigurationTestHelper( + getActivity(), this, this, settings, false); + configurationTestHelper.test(); + + return true; + } + case R.string.pref_key_sync_syncTypes_description: { + Activity activity = getActivity(); + if (activity != null) { + new AlertDialog.Builder(activity) + .setTitle(R.string.pref_name_sync_syncTypes) + .setMessage(R.string.pref_desc_sync_syncTypes_text) + .setPositiveButton(R.string.ok, null) + .show(); + } + return true; + } + case R.string.pref_key_ui_disableTouch_keyCode: { + showDisableTouchSetKeyCodeDialog(); + return true; + } + case R.string.pref_key_misc_wipeDB: { + Activity activity = getActivity(); + if (activity != null) { + new AlertDialog.Builder(activity) + .setTitle(R.string.pref_name_misc_wipeDB_confirmTitle) + .setMessage(R.string.pref_name_misc_wipeDB_confirmMessage) + .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + OperationsHelper.wipeDB(App.getSettings()); + } + }) + .setNegativeButton(R.string.negative_answer, null) + .show(); + } + return true; + } + case R.string.pref_key_misc_localQueue_dumpToFile: { + dumpOfflineQueue(); + return true; + } + case R.string.pref_key_misc_localQueue_removeFirstItem: { + removeFirstOfflineQueueItem(); + return true; + } + case R.string.pref_key_misc_logging_logcatToFile: { + Activity activity = getActivity(); + if (activity != null) { + LoggingUtils.saveLogcatToFile(activity); + } + return true; + } + } + + return false; + } + + private void dumpOfflineQueue() { + Activity activity = getActivity(); + if (activity == null) return; + + QueueHelper queueHelper = new QueueHelper(DbConnection.getSession()); + List items = queueHelper.getQueueItems(); + if (items.isEmpty()) { + Toast.makeText(activity, R.string.misc_localQueue_empty, Toast.LENGTH_SHORT).show(); + return; + } + + String string = getString(R.string.misc_localQueue_dumpToFile_header, + getString(R.string.issues_url)) + "\r\n\r\n" + + queueItemsToString(items); + + try { + File file = StorageHelper.dumpQueueData(string); + Toast.makeText(activity, getString(R.string.misc_localQueue_dumpToFile_result_dumped, + file.getAbsolutePath()), Toast.LENGTH_LONG).show(); + } catch (Exception e) { + Log.w(TAG, "Error during dumping offline queue", e); + Toast.makeText(activity, getString(R.string.misc_localQueue_dumpToFile_result_error, + e.toString()), Toast.LENGTH_LONG).show(); + } + } + + private String queueItemsToString(List items) { + String nl = "\r\n"; + String delim = "; "; + + StringBuilder sb = new StringBuilder(); + + sb.append("id").append(delim) + .append("action").append(delim) + .append("articleId").append(delim) + .append("localArticleId").append(delim) + .append("extra").append(delim) + .append("extra2").append(nl); + + for (QueueItem item : items) { + sb.append(item.getId()).append(delim) + .append(item.getAction()).append(delim) + .append(item.getArticleId()).append(delim) + .append(item.getLocalArticleId()).append(delim) + .append(item.getExtra()).append(delim) + .append(item.getExtra2()).append(nl); + } + + return sb.toString(); + } + + private void removeFirstOfflineQueueItem() { + Activity activity = getActivity(); + if (activity == null) return; + + QueueHelper queueHelper = new QueueHelper(DbConnection.getSession()); + List items = queueHelper.getQueueItems(); + if (items.isEmpty()) { + Toast.makeText(activity, R.string.misc_localQueue_empty, Toast.LENGTH_SHORT).show(); + return; + } + + queueHelper.dequeueItems(Collections.singletonList(items.get(0))); + Toast.makeText(activity, R.string.misc_localQueue_removeFirstItem_done, Toast.LENGTH_SHORT).show(); + } + + private void showDisableTouchSetKeyCodeDialog() { + Activity activity = getActivity(); + if (activity != null) { + AlertDialog.Builder builder = new AlertDialog.Builder(activity); + builder.setTitle(R.string.d_disableTouch_changeKey_title); + + @SuppressLint("InflateParams") final View view = activity.getLayoutInflater().inflate(R.layout.dialog_set_key, null); + final TextView keyCodeTextView = (TextView) view.findViewById(R.id.tv_keyCode); + + setIntToTextView(keyCodeTextView, settings.getDisableTouchKeyCode()); + + builder.setView(view); + + DialogInterface.OnKeyListener keyListener = new DialogInterface.OnKeyListener() { + @Override + public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { + if (event.getAction() != KeyEvent.ACTION_DOWN) return false; + + setIntToTextView(keyCodeTextView, keyCode); + + return false; + } + }; + + builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + try { + settings.setDisableTouchKeyCode(Integer.parseInt( + keyCodeTextView.getText().toString())); + } catch (NumberFormatException ignored) { + } + } + }); + builder.setNegativeButton(android.R.string.cancel, null); + builder.setOnKeyListener(keyListener); + + builder.show(); + } + } + + @SuppressLint("SetTextI18n") + private void setIntToTextView(TextView textView, int value) { + textView.setText(Integer.toString(value)); + } + + @Override + public void onGetCredentialsResult(ClientCredentials clientCredentials) { + setTextPreference(R.string.pref_key_connection_api_clientID, + clientCredentials.clientID); + setTextPreference(R.string.pref_key_connection_api_clientSecret, + clientCredentials.clientSecret); + } + + @Override + public void onGetCredentialsFail() { + } + + @Override + public void onConfigurationTestSuccess(String url) { + Log.d(TAG, String.format("onConfigurationTestSuccess(%s)", url)); + + if (url != null) { + setTextPreference(R.string.pref_key_connection_url, url); + } + + settings.setConfigurationOk(true); + settings.setConfigurationErrorShown(false); + + invalidateConfiguration = false; + + Toast.makeText(getActivity(), R.string.settings_parametersAutofilled, + Toast.LENGTH_SHORT).show(); + } + + @Override + public void onConnectionTestFail(WallabagWebService.ConnectionTestResult result, + String details) { + } + + @Override + public void onApiAccessTestFail(TestApiAccessTask.Result result, String details) { + } + + private void setOnClickListener(int keyResID) { + Preference preference = findPreference(getString(keyResID)); + if (preference != null) { + preference.setOnPreferenceClickListener(this); + } + } + + private void setTextPreference(int preferenceID, String value) { + EditTextPreference preference = (EditTextPreference) + findPreference(getString(preferenceID)); + + if (preference != null) { + preference.setText(value); + } + } + + private void updateSummary(int keyResID) { + String key = getString(keyResID); + + switch (keyResID) { + case R.string.pref_key_connection_url: + EditTextPreference preference = (EditTextPreference) + findPreference(key); + if (preference != null) { + String value = preference.getText(); + setSummary(key, (value == null || value.isEmpty()) + ? getString(R.string.pref_desc_connection_url) : value); + } + break; + + case R.string.pref_key_connection_username: + case R.string.pref_key_connection_api_clientID: + case R.string.pref_key_connection_advanced_httpAuthUsername: + case R.string.pref_key_ui_article_fontSize: + case R.string.pref_key_ui_screenScrolling_percent: + setEditTextSummaryFromContent(key); + break; + + case R.string.pref_key_ui_theme: + case R.string.pref_key_autoSync_interval: + case R.string.pref_key_autoSync_type: + setListSummaryFromContent(key); + break; + + case R.string.pref_key_connection_password: + case R.string.pref_key_connection_api_clientSecret: + case R.string.pref_key_connection_advanced_httpAuthPassword: + setPasswordSummary(key); + break; + + case R.string.pref_key_storage_dbPath: + ListPreference dbPathListPreference = (ListPreference) findPreference( + getString(R.string.pref_key_storage_dbPath)); + if (dbPathListPreference != null) { + CharSequence value = dbPathListPreference.getEntry(); + if (TextUtils.isEmpty(value)) { + dbPathListPreference.setSummary(R.string.pref_name_storage_dbPath_internalStorage); + } else if (value.equals(StorageHelper.getExternalStoragePath())) { + dbPathListPreference.setSummary(R.string.pref_name_storage_dbPath_externalStorage); + } else { + dbPathListPreference.setSummary(value); + } + } + break; + } + } + + private void setSummary(String key, String text) { + Preference preference = findPreference(key); + if (preference != null) { + preference.setSummary(text); + } + } + + private void setEditTextSummaryFromContent(String key) { + EditTextPreference preference = (EditTextPreference) findPreference(key); + if (preference != null) { + preference.setSummary(preference.getText()); + } + } + + private void setListSummaryFromContent(String key) { + ListPreference preference = (ListPreference) findPreference(key); + if (preference != null) { + preference.setSummary(preference.getEntry()); + } + } + + private void setPasswordSummary(String key) { + EditTextPreference preference = (EditTextPreference) findPreference(key); + if (preference != null) { + String value = preference.getText(); + preference.setSummary(value == null || value.isEmpty() ? "" : "********"); + } + } + +} diff --git a/app/src/main/res/drawable-hdpi/ic_action_next_item.png b/app/src/main/res/drawable-hdpi/ic_action_next_item.png deleted file mode 100644 index fc998a511..000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_action_next_item.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_action_previous_item.png b/app/src/main/res/drawable-hdpi/ic_action_previous_item.png deleted file mode 100644 index 10fcec8a6..000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_action_previous_item.png and /dev/null differ diff --git a/app/src/main/res/drawable-hdpi/ic_action_refresh.png b/app/src/main/res/drawable-hdpi/ic_action_refresh.png deleted file mode 100644 index dae27903e..000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_action_refresh.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_refresh.png b/app/src/main/res/drawable-mdpi/ic_action_refresh.png deleted file mode 100644 index 94ab6f4c5..000000000 Binary files a/app/src/main/res/drawable-mdpi/ic_action_refresh.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_action_refresh.png b/app/src/main/res/drawable-xhdpi/ic_action_refresh.png deleted file mode 100644 index ab4ab9da6..000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_action_refresh.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_refresh.png b/app/src/main/res/drawable-xxhdpi/ic_action_refresh.png deleted file mode 100644 index 44ee117ee..000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_action_refresh.png and /dev/null differ diff --git a/app/src/main/res/drawable/ic_action_next_item.xml b/app/src/main/res/drawable/ic_action_next_item.xml new file mode 100644 index 000000000..4fff2475b --- /dev/null +++ b/app/src/main/res/drawable/ic_action_next_item.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_action_previous_item.xml b/app/src/main/res/drawable/ic_action_previous_item.xml new file mode 100644 index 000000000..1805b7d15 --- /dev/null +++ b/app/src/main/res/drawable/ic_action_previous_item.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_add_black_24dp.xml b/app/src/main/res/drawable/ic_add_black_24dp.xml index 0258249cc..b7613ff55 100644 --- a/app/src/main/res/drawable/ic_add_black_24dp.xml +++ b/app/src/main/res/drawable/ic_add_black_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_back.xml b/app/src/main/res/drawable/ic_back.xml new file mode 100644 index 000000000..bab545a70 --- /dev/null +++ b/app/src/main/res/drawable/ic_back.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_delete_black_24dp.xml b/app/src/main/res/drawable/ic_delete_black_24dp.xml index 39e64d698..bc3a24e94 100644 --- a/app/src/main/res/drawable/ic_delete_black_24dp.xml +++ b/app/src/main/res/drawable/ic_delete_black_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_done_black_24dp.xml b/app/src/main/res/drawable/ic_done_black_24dp.xml index 83ee7bb4e..6d945f8fc 100644 --- a/app/src/main/res/drawable/ic_done_black_24dp.xml +++ b/app/src/main/res/drawable/ic_done_black_24dp.xml @@ -3,7 +3,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24.0" - android:viewportHeight="24.0"> + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_done_solarized_24dp.xml b/app/src/main/res/drawable/ic_done_solarized_24dp.xml index 7196f0d21..3078ff3c9 100644 --- a/app/src/main/res/drawable/ic_done_solarized_24dp.xml +++ b/app/src/main/res/drawable/ic_done_solarized_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_done_white_24dp.xml b/app/src/main/res/drawable/ic_done_white_24dp.xml index 6541ee3e3..6d1c537f5 100644 --- a/app/src/main/res/drawable/ic_done_white_24dp.xml +++ b/app/src/main/res/drawable/ic_done_white_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_fast_forward_24dp.xml b/app/src/main/res/drawable/ic_fast_forward_24dp.xml index fd9249f61..6b9454591 100644 --- a/app/src/main/res/drawable/ic_fast_forward_24dp.xml +++ b/app/src/main/res/drawable/ic_fast_forward_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_fast_forward_solarized_24dp.xml b/app/src/main/res/drawable/ic_fast_forward_solarized_24dp.xml index 729486e97..30de25d58 100644 --- a/app/src/main/res/drawable/ic_fast_forward_solarized_24dp.xml +++ b/app/src/main/res/drawable/ic_fast_forward_solarized_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_fast_forward_white_24dp.xml b/app/src/main/res/drawable/ic_fast_forward_white_24dp.xml index 8d1f95075..0b1da9ea6 100644 --- a/app/src/main/res/drawable/ic_fast_forward_white_24dp.xml +++ b/app/src/main/res/drawable/ic_fast_forward_white_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_fast_rewind_24dp.xml b/app/src/main/res/drawable/ic_fast_rewind_24dp.xml index 6d7685e7a..f7ac1eb71 100644 --- a/app/src/main/res/drawable/ic_fast_rewind_24dp.xml +++ b/app/src/main/res/drawable/ic_fast_rewind_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_fast_rewind_solarized_24dp.xml b/app/src/main/res/drawable/ic_fast_rewind_solarized_24dp.xml index 4a557ab56..614b10820 100644 --- a/app/src/main/res/drawable/ic_fast_rewind_solarized_24dp.xml +++ b/app/src/main/res/drawable/ic_fast_rewind_solarized_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_fast_rewind_white_24dp.xml b/app/src/main/res/drawable/ic_fast_rewind_white_24dp.xml index fc6addd01..54780a53d 100644 --- a/app/src/main/res/drawable/ic_fast_rewind_white_24dp.xml +++ b/app/src/main/res/drawable/ic_fast_rewind_white_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_file_download_24dp.xml b/app/src/main/res/drawable/ic_file_download_24dp.xml index e43b8645a..98547e58f 100644 --- a/app/src/main/res/drawable/ic_file_download_24dp.xml +++ b/app/src/main/res/drawable/ic_file_download_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_info_outline_black_24dp.xml b/app/src/main/res/drawable/ic_info_outline_black_24dp.xml index cf53e145c..7734fa8db 100644 --- a/app/src/main/res/drawable/ic_info_outline_black_24dp.xml +++ b/app/src/main/res/drawable/ic_info_outline_black_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_list_black_24dp.xml b/app/src/main/res/drawable/ic_list_black_24dp.xml index 4c2fb8834..51c2ad8d8 100644 --- a/app/src/main/res/drawable/ic_list_black_24dp.xml +++ b/app/src/main/res/drawable/ic_list_black_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_menu_24dp.xml b/app/src/main/res/drawable/ic_menu_24dp.xml index 84b1d3b4f..89935d236 100644 --- a/app/src/main/res/drawable/ic_menu_24dp.xml +++ b/app/src/main/res/drawable/ic_menu_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_menu_solarized_24dp.xml b/app/src/main/res/drawable/ic_menu_solarized_24dp.xml index ceaac1eb3..eab6bd089 100644 --- a/app/src/main/res/drawable/ic_menu_solarized_24dp.xml +++ b/app/src/main/res/drawable/ic_menu_solarized_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_menu_white_24dp.xml b/app/src/main/res/drawable/ic_menu_white_24dp.xml index 0b6cfbc50..8657e8c1f 100644 --- a/app/src/main/res/drawable/ic_menu_white_24dp.xml +++ b/app/src/main/res/drawable/ic_menu_white_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_more_horiz_24dp.xml b/app/src/main/res/drawable/ic_more_horiz_24dp.xml index 2acd55b29..4519a704d 100644 --- a/app/src/main/res/drawable/ic_more_horiz_24dp.xml +++ b/app/src/main/res/drawable/ic_more_horiz_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_musical_notes_24dp.xml b/app/src/main/res/drawable/ic_musical_notes_24dp.xml index 8b3bd637d..ffc29e6e6 100644 --- a/app/src/main/res/drawable/ic_musical_notes_24dp.xml +++ b/app/src/main/res/drawable/ic_musical_notes_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_musical_notes_solarized_24dp.xml b/app/src/main/res/drawable/ic_musical_notes_solarized_24dp.xml index f969f33f3..d3e53558b 100644 --- a/app/src/main/res/drawable/ic_musical_notes_solarized_24dp.xml +++ b/app/src/main/res/drawable/ic_musical_notes_solarized_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_musical_notes_white_24dp.xml b/app/src/main/res/drawable/ic_musical_notes_white_24dp.xml index 3f8d603e3..1a6917b26 100644 --- a/app/src/main/res/drawable/ic_musical_notes_white_24dp.xml +++ b/app/src/main/res/drawable/ic_musical_notes_white_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_open_in_new_black_24dp.xml b/app/src/main/res/drawable/ic_open_in_new_black_24dp.xml index 60b75a549..ef5bc0188 100644 --- a/app/src/main/res/drawable/ic_open_in_new_black_24dp.xml +++ b/app/src/main/res/drawable/ic_open_in_new_black_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_open_in_new_white_24dp.xml b/app/src/main/res/drawable/ic_open_in_new_white_24dp.xml index 3a81295ea..6ea44bd0b 100644 --- a/app/src/main/res/drawable/ic_open_in_new_white_24dp.xml +++ b/app/src/main/res/drawable/ic_open_in_new_white_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_pause_24dp.xml b/app/src/main/res/drawable/ic_pause_24dp.xml index 53b5e6a9a..e658bc269 100644 --- a/app/src/main/res/drawable/ic_pause_24dp.xml +++ b/app/src/main/res/drawable/ic_pause_24dp.xml @@ -2,7 +2,8 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24.0" - android:viewportHeight="24.0"> + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_play_arrow_24dp.xml b/app/src/main/res/drawable/ic_play_arrow_24dp.xml index bf9b895ac..95aaa85c7 100644 --- a/app/src/main/res/drawable/ic_play_arrow_24dp.xml +++ b/app/src/main/res/drawable/ic_play_arrow_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_play_arrow_solarized_24dp.xml b/app/src/main/res/drawable/ic_play_arrow_solarized_24dp.xml index d0fe86659..cc96db17e 100644 --- a/app/src/main/res/drawable/ic_play_arrow_solarized_24dp.xml +++ b/app/src/main/res/drawable/ic_play_arrow_solarized_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_play_arrow_white_24dp.xml b/app/src/main/res/drawable/ic_play_arrow_white_24dp.xml index 81a8f74f6..164220269 100644 --- a/app/src/main/res/drawable/ic_play_arrow_white_24dp.xml +++ b/app/src/main/res/drawable/ic_play_arrow_white_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_redo_24dp.xml b/app/src/main/res/drawable/ic_redo_24dp.xml index 424e788f5..7871db65e 100644 --- a/app/src/main/res/drawable/ic_redo_24dp.xml +++ b/app/src/main/res/drawable/ic_redo_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_redo_solarized_24dp.xml b/app/src/main/res/drawable/ic_redo_solarized_24dp.xml index a13a51b84..74b1aa15f 100644 --- a/app/src/main/res/drawable/ic_redo_solarized_24dp.xml +++ b/app/src/main/res/drawable/ic_redo_solarized_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_redo_white_24dp.xml b/app/src/main/res/drawable/ic_redo_white_24dp.xml index 3dfe92985..febaa27c9 100644 --- a/app/src/main/res/drawable/ic_redo_white_24dp.xml +++ b/app/src/main/res/drawable/ic_redo_white_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> diff --git a/app/src/main/res/drawable/ic_running_24dp.xml b/app/src/main/res/drawable/ic_running_24dp.xml index e88d5160f..2c0772165 100644 --- a/app/src/main/res/drawable/ic_running_24dp.xml +++ b/app/src/main/res/drawable/ic_running_24dp.xml @@ -1,8 +1,9 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0" + android:tint="?colorControlNormal"> - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - + - - @drawable/ic_done_white_24dp - @drawable/ic_undo_white_24dp - @drawable/ic_star_border_white_24dp - @drawable/ic_star_white_24dp - @drawable/ic_tag_white_24dp - @drawable/ic_open_in_new_white_24dp + - - + diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml new file mode 100644 index 000000000..2fe5c12ca --- /dev/null +++ b/app/src/main/res/values/themes.xml @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 9b88a4711..3e4d42491 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -78,7 +78,7 @@ android:key="@string/pref_key_ui_theme" android:title="@string/pref_name_ui_theme" android:dialogTitle="@string/pref_name_ui_theme" - android:defaultValue="LIGHT"/> + android:defaultValue="DEFAULT"/>