diff --git a/app/src/main/java/net/gsantner/opoc/ui/SearchOrCustomTextDialog.java b/app/src/main/java/net/gsantner/opoc/ui/SearchOrCustomTextDialog.java index 7ba77cea..f0558275 100644 --- a/app/src/main/java/net/gsantner/opoc/ui/SearchOrCustomTextDialog.java +++ b/app/src/main/java/net/gsantner/opoc/ui/SearchOrCustomTextDialog.java @@ -26,6 +26,7 @@ import android.support.design.widget.Snackbar; import android.support.v7.app.AlertDialog; import android.support.v7.widget.AppCompatEditText; import android.text.Editable; +import android.text.InputType; import android.text.Spannable; import android.text.SpannableString; import android.text.TextUtils; @@ -75,7 +76,7 @@ public class SearchOrCustomTextDialog { public int dialogWidthDp = WindowManager.LayoutParams.MATCH_PARENT; public int dialogHeightDp = WindowManager.LayoutParams.WRAP_CONTENT; public int gravity = Gravity.NO_GRAVITY; - public int searchInputType = 0; + public int searchInputType = InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS; public boolean searchIsRegex = false; public Callback.a1 highlighter; public String extraFilter = null; @@ -99,21 +100,20 @@ public class SearchOrCustomTextDialog { } private static class WithPositionAdapter extends ArrayAdapter> { + @LayoutRes + final int _layout; + final LayoutInflater _inflater; + final DialogOptions _dopt; + final List> _filteredItems; + final Pattern _extraPattern; - final LayoutInflater mInflater; - final @LayoutRes - int mLayout; - final DialogOptions dopt; - final List> filteredItems; - final Pattern extraPattern; - - WithPositionAdapter(Context context, @LayoutRes int layout, List> filteredItems, DialogOptions dopt) { - super(context, layout, filteredItems); - mInflater = LayoutInflater.from(context); - mLayout = layout; - this.dopt = dopt; - this.filteredItems = filteredItems; - extraPattern = dopt.extraFilter == null ? null : Pattern.compile(dopt.extraFilter); + WithPositionAdapter(Context c_context, @LayoutRes int c_layout, List> c_filteredItems, DialogOptions c_dopt) { + super(c_context, c_layout, c_filteredItems); + _inflater = LayoutInflater.from(c_context); + _layout = c_layout; + _dopt = c_dopt; + _filteredItems = c_filteredItems; + _extraPattern = (c_dopt.extraFilter == null ? null : Pattern.compile(c_dopt.extraFilter)); } @NonNull @@ -125,30 +125,30 @@ public class SearchOrCustomTextDialog { final TextView textView; if (convertView == null) { - textView = (TextView) mInflater.inflate(mLayout, parent, false); + textView = (TextView) _inflater.inflate(_layout, parent, false); } else { textView = (TextView) convertView; } - if (posInOriginalList >= 0 && dopt.iconsForData != null && posInOriginalList < dopt.iconsForData.size() && dopt.iconsForData.get(posInOriginalList) != 0) { - textView.setCompoundDrawablesWithIntrinsicBounds(dopt.iconsForData.get(posInOriginalList), 0, 0, 0); + if (posInOriginalList >= 0 && _dopt.iconsForData != null && posInOriginalList < _dopt.iconsForData.size() && _dopt.iconsForData.get(posInOriginalList) != 0) { + textView.setCompoundDrawablesWithIntrinsicBounds(_dopt.iconsForData.get(posInOriginalList), 0, 0, 0); textView.setCompoundDrawablePadding(32); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - textView.setCompoundDrawableTintList(ColorStateList.valueOf(dopt.isDarkDialog ? Color.WHITE : Color.BLACK)); + textView.setCompoundDrawableTintList(ColorStateList.valueOf(_dopt.isDarkDialog ? Color.WHITE : Color.BLACK)); } } else { textView.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); } - if (dopt.highlightData != null) { - final boolean hl = dopt.highlightData.contains(text); - textView.setTextColor(hl ? dopt.highlightColor : dopt.textColor); + if (_dopt.highlightData != null) { + final boolean hl = _dopt.highlightData.contains(text); + textView.setTextColor(hl ? _dopt.highlightColor : _dopt.textColor); textView.setTypeface(null, hl ? Typeface.BOLD : Typeface.NORMAL); } - if (dopt.highlighter != null) { + if (_dopt.highlighter != null) { Spannable s = new SpannableString(text); - dopt.highlighter.callback(s); + _dopt.highlighter.callback(s); textView.setText(s); } else { textView.setText(text); @@ -163,8 +163,8 @@ public class SearchOrCustomTextDialog { @SuppressWarnings("unchecked") @Override protected void publishResults(final CharSequence constraint, final FilterResults results) { - filteredItems.clear(); - filteredItems.addAll((List>) results.values); + _filteredItems.clear(); + _filteredItems.addAll((List>) results.values); notifyDataSetChanged(); } @@ -172,14 +172,14 @@ public class SearchOrCustomTextDialog { protected FilterResults performFiltering(final CharSequence constraint) { final ArrayList> resList = new ArrayList<>(); - if (dopt.data != null) { + if (_dopt.data != null) { final String fil = constraint.toString(); final boolean emptySearch = fil.isEmpty(); - for (int i = 0; i < dopt.data.size(); i++) { - final CharSequence str = dopt.data.get(i); - final boolean matchExtra = (extraPattern == null) || extraPattern.matcher(str).find(); + for (int i = 0; i < _dopt.data.size(); i++) { + final CharSequence str = _dopt.data.get(i); + final boolean matchExtra = (_extraPattern == null) || _extraPattern.matcher(str).find(); final boolean matchNormal = str.toString().toLowerCase(Locale.getDefault()).contains(fil.toLowerCase(Locale.getDefault())); - final boolean matchRegex = dopt.searchIsRegex && (str.toString().matches(fil)); + final boolean matchRegex = _dopt.searchIsRegex && (str.toString().matches(fil)); if (matchExtra && (matchNormal || matchRegex || emptySearch)) { resList.add(new Pair<>(str, i)); } diff --git a/app/src/main/java/net/gsantner/opoc/util/ActivityUtils.java b/app/src/main/java/net/gsantner/opoc/util/ActivityUtils.java index 024d0f2f..41c86e17 100644 --- a/app/src/main/java/net/gsantner/opoc/util/ActivityUtils.java +++ b/app/src/main/java/net/gsantner/opoc/util/ActivityUtils.java @@ -13,7 +13,6 @@ package net.gsantner.opoc.util; import android.app.Activity; import android.content.ActivityNotFoundException; import android.content.ComponentName; -import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; @@ -39,7 +38,7 @@ import android.webkit.WebView; import android.widget.ScrollView; -@SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue", "SpellCheckingInspection"}) +@SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue", "SpellCheckingInspection", "rawtypes", "UnusedReturnValue"}) public class ActivityUtils extends net.gsantner.opoc.util.ContextUtils { //######################## //## Members, Constructors @@ -239,13 +238,22 @@ public class ActivityUtils extends net.gsantner.opoc.util.ContextUtils { } public ActivityUtils setLauncherActivityEnabled(Class activityClass, boolean enable) { - Context context = _context.getApplicationContext(); - PackageManager pkg = context.getPackageManager(); - ComponentName component = new ComponentName(context, activityClass); - pkg.setComponentEnabledSetting(component, enable ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); + try { + ComponentName component = new ComponentName(_context, activityClass); + _context.getPackageManager().setComponentEnabledSetting(component, enable ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); + } catch (Exception ignored) { + } return this; } + public boolean isLauncherEnabled(Class activityClass) { + try { + ComponentName component = new ComponentName(_context, activityClass); + return _context.getPackageManager().getComponentEnabledSetting(component) != PackageManager.COMPONENT_ENABLED_STATE_DISABLED; + } catch (Exception ignored) { + } + return false; + } @ColorInt public Integer getCurrentPrimaryColor() { diff --git a/app/src/main/java/net/gsantner/opoc/util/AdBlock.java b/app/src/main/java/net/gsantner/opoc/util/AdBlock.java index 304a4b85..5045d0f2 100644 --- a/app/src/main/java/net/gsantner/opoc/util/AdBlock.java +++ b/app/src/main/java/net/gsantner/opoc/util/AdBlock.java @@ -46,8 +46,9 @@ import java.util.Set; /** * Simple Host-Based AdBlocker */ -@SuppressWarnings({"WeakerAccess", "SpellCheckingInspection", "unused"}) +@SuppressWarnings({"WeakerAccess", "SpellCheckingInspection", "unused", "TryFinallyCanBeTryWithResources"}) public class AdBlock { + private static final Object synchronizeObj = new Object(); private static final AdBlock instance = new AdBlock(); public static AdBlock getInstance() { @@ -61,7 +62,9 @@ public class AdBlock { //######################## private final Set _adblockHostsFromRaw = new HashSet<>(); private final Set _adblockHosts = new HashSet<>(); - private boolean _isLoaded; + private final List> _customBlockCallbacks = new ArrayList<>(); + private boolean _isLoaded = false; + private boolean _isAdblockLogging = false; //######################## //## @@ -72,25 +75,47 @@ public class AdBlock { } public boolean isAdHost(String urlS) { + boolean block = false; if (urlS != null && !urlS.isEmpty() && urlS.startsWith("http")) { try { - URI url = new URI(urlS); + URI url; + try { + url = new URI(urlS); + } catch (Exception e) { + url = new URI(urlS.replaceFirst("[?].*", "")); + } String host = url.getHost().trim(); if (host.startsWith("www.") && host.length() >= 4) { host = host.substring(4); } - return _adblockHosts.contains(host) || _adblockHosts.contains("www." + host); + block = _adblockHosts.contains(host) || _adblockHosts.contains("www." + host); + for (Callback.b3 cb : _customBlockCallbacks) { + if (block) { + break; + } + try { + block = cb.callback(url, urlS, host); + } catch (Exception ignored) { + } + } } catch (URISyntaxException e) { e.printStackTrace(); } } - return false; + + if (_isAdblockLogging) { + Log.d(getClass().getSimpleName(), "UrlAllowed-" + (block ? "N" : "Y") + " " + urlS); + } + return block; } public AdBlock reset() { - _adblockHosts.clear(); - _adblockHosts.addAll(_adblockHostsFromRaw); + synchronized (synchronizeObj) { + _adblockHosts.clear(); + _adblockHosts.addAll(_adblockHostsFromRaw); + _customBlockCallbacks.clear(); + } return this; } @@ -102,7 +127,7 @@ public class AdBlock { return new WebResourceResponse("text/plain", "utf-8", new ByteArrayInputStream("".getBytes())); } - public void addBlockedHosts(String... hosts) { + public AdBlock addBlockedHosts(String... hosts) { for (String host : hosts) { if (host != null) { host = host.trim(); @@ -110,23 +135,29 @@ public class AdBlock { host = host.substring(4); } if (!host.startsWith("#") && !host.startsWith("\"")) { - _adblockHosts.add(host); + synchronized (synchronizeObj) { + _adblockHosts.add(host); + } } } } - + return this; } - public void loadHostsFromRawAssetsAsync(final Context context) { - new Thread(new Runnable() { - @Override - public void run() { - try { + public void loadHostsFromRawAssetsAsync(final Context context, final boolean... debugIgnoreAssets) { + if (debugIgnoreAssets != null && debugIgnoreAssets.length > 0 && debugIgnoreAssets[0]) { + _isLoaded = true; + return; + } + + new Thread(() -> { + try { + synchronized (synchronizeObj) { loadHostsFromRawAssets(context); _isLoaded = true; - } catch (IOException e) { - e.printStackTrace(); } + } catch (IOException e) { + e.printStackTrace(); } }).start(); } @@ -172,4 +203,17 @@ public class AdBlock { } return adblockResIds; } + + // URI uri, String url, String host + public AdBlock addCustomBlockCallback(Callback.b3 cb) { + synchronized (synchronizeObj) { + _customBlockCallbacks.add(cb); + } + return this; + } + + public AdBlock setLogEnabled(boolean isAdblockLogging) { + _isAdblockLogging = isAdblockLogging; + return this; + } } diff --git a/app/src/main/java/net/gsantner/opoc/util/ContextUtils.java b/app/src/main/java/net/gsantner/opoc/util/ContextUtils.java index cab0128d..e2949db7 100644 --- a/app/src/main/java/net/gsantner/opoc/util/ContextUtils.java +++ b/app/src/main/java/net/gsantner/opoc/util/ContextUtils.java @@ -67,7 +67,9 @@ import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; +import android.view.Surface; import android.view.View; +import android.view.WindowManager; import android.webkit.MimeTypeMap; import android.widget.ImageView; import android.widget.TextView; @@ -90,7 +92,7 @@ import static android.content.Context.VIBRATOR_SERVICE; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.graphics.Bitmap.CompressFormat; -@SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue", "ObsoleteSdkInt", "deprecation", "SpellCheckingInspection", "TryFinallyCanBeTryWithResources", "UnusedAssignment"}) +@SuppressWarnings({"WeakerAccess", "unused", "SameParameterValue", "ObsoleteSdkInt", "deprecation", "SpellCheckingInspection", "TryFinallyCanBeTryWithResources", "UnusedAssignment", "UnusedReturnValue"}) public class ContextUtils { // // Members, Constructors @@ -257,7 +259,7 @@ public class ContextUtils { * Send a {@link Intent#ACTION_VIEW} Intent with given paramter * If the parameter is an string a browser will get triggered */ - public void openWebpageInExternalBrowser(final String url) { + public ContextUtils openWebpageInExternalBrowser(final String url) { try { Uri uri = Uri.parse(url); Intent intent = new Intent(Intent.ACTION_VIEW, uri); @@ -266,6 +268,7 @@ public class ContextUtils { } catch (Exception e) { e.printStackTrace(); } + return this; } /** @@ -1011,6 +1014,25 @@ public class ContextUtils { vibrator.vibrate(ms_v); } } + + /* + Check if Wifi is connected. Requires these permissions in AndroidManifest: + + + */ + @SuppressLint("MissingPermission") + public boolean isWifiConnected(boolean... enabledOnly) { + final boolean doEnabledCheckOnly = enabledOnly != null && enabledOnly.length > 0 && enabledOnly[0]; + final ConnectivityManager connectivityManager = (ConnectivityManager) _context.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE); + final NetworkInfo wifiInfo = connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI); + return wifiInfo != null && (doEnabledCheckOnly ? wifiInfo.isAvailable() : wifiInfo.isConnected()); + } + + // Returns if the device is currently in portrait orientation (landscape=false) + public boolean isDeviceOrientationPortrait() { + final int rotation = ((WindowManager) _context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getOrientation(); + return (rotation == Surface.ROTATION_0) || (rotation == Surface.ROTATION_180); + } } diff --git a/app/src/main/java/net/gsantner/opoc/util/FileUtils.java b/app/src/main/java/net/gsantner/opoc/util/FileUtils.java index 4e96a8ec..22e3da39 100644 --- a/app/src/main/java/net/gsantner/opoc/util/FileUtils.java +++ b/app/src/main/java/net/gsantner/opoc/util/FileUtils.java @@ -501,4 +501,11 @@ public class FileUtils { return String.format(Locale.getDefault(), "%.2f%s", (bytes / 1000000000f), "TB"); } } + + public static File join(File file, String... childSegments) { + for (final String s : childSegments != null ? childSegments : new String[0]) { + file = new File(file, s); + } + return file; + } } diff --git a/app/src/main/java/net/gsantner/opoc/util/NetworkUtils.java b/app/src/main/java/net/gsantner/opoc/util/NetworkUtils.java index 08e36948..09d6795b 100644 --- a/app/src/main/java/net/gsantner/opoc/util/NetworkUtils.java +++ b/app/src/main/java/net/gsantner/opoc/util/NetworkUtils.java @@ -24,7 +24,6 @@ import java.net.URL; import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.Charset; -import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; @@ -222,4 +221,14 @@ public class NetworkUtils { return result; } + + public static void httpGetAsync(final String url, final Callback.a1 callback) { + new Thread(() -> { + try { + String c = NetworkUtils.performCall(url, GET); + callback.callback(c); + } catch (Exception ignored) { + } + }).start(); + } }