Unverified Commit a618da97 authored by gsantner's avatar gsantner 🦀
Browse files

Update opoc

parent 3ec8ab89
......@@ -108,11 +108,13 @@ dependencies {
implementation "com.android.support:support-v4:${version_library_appcompat}"
implementation "com.android.support:customtabs:${version_library_appcompat}"
implementation "com.android.support:cardview-v7:${version_library_appcompat}"
implementation "com.android.support:preference-v7:${version_library_appcompat}"
// UI libraries
implementation "com.github.DASAR:ShiftColorPicker:v0.5"
// Tool libraries
implementation 'commons-io:commons-io:2.6'
implementation "info.guardianproject.netcipher:netcipher:${version_library_netcipher}"
implementation "info.guardianproject.netcipher:netcipher-webkit:${version_library_netcipher}"
implementation "com.jakewharton:butterknife:${version_library_butterknife}"
......
......@@ -16,7 +16,10 @@ import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.view.ViewGroup;
......@@ -33,6 +36,7 @@ public abstract class GsFragmentBase extends Fragment {
protected ContextUtils _cu;
protected Bundle _savedInstanceState = null;
protected Menu _fragmentMenu;
@Override
public void onCreate(Bundle savedInstanceState) {
......@@ -51,6 +55,9 @@ public abstract class GsFragmentBase extends Fragment {
_cu = new ContextUtils(inflater.getContext());
_cu.setAppLanguage(getAppLanguage());
_savedInstanceState = savedInstanceState;
if (getLayoutResId() == 0) {
Log.e(getClass().getCanonicalName(), "Error: GsFragmentbase.onCreateview: Returned 0 for getLayoutResId");
}
View view = inflater.inflate(getLayoutResId(), container, false);
ButterKnife.bind(this, view);
return view;
......@@ -126,4 +133,14 @@ public abstract class GsFragmentBase extends Fragment {
}
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
_fragmentMenu = menu;
}
public Menu getFragmentMenu() {
return _fragmentMenu;
}
}
......@@ -10,8 +10,6 @@
#########################################################*/
package net.gsantner.opoc.preference;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
@SuppressWarnings({"UnusedReturnValue", "SpellCheckingInspection", "unused", "SameParameterValue"})
......
......@@ -532,7 +532,7 @@ public class SharedPreferencesPropertyBackend implements PropertyBackend<String,
* A method to determine if current hour is between begin and end.
* This is especially useful for time-based light/dark mode
*/
public boolean isCurrentHourOfDayBetween(int begin, int end) {
public static boolean isCurrentHourOfDayBetween(int begin, int end) {
begin = (begin >= 23 || begin < 0) ? 0 : begin;
end = (end >= 23 || end < 0) ? 0 : end;
int h = Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
......@@ -559,7 +559,7 @@ public class SharedPreferencesPropertyBackend implements PropertyBackend<String,
public boolean afterDaysTrue(String key, int daysSinceLastTime, int firstTime, final SharedPreferences... pref) {
Date d = new Date(System.currentTimeMillis());
if (!contains(key)) {
d = getDateOfDaysAgo(daysSinceLastTime-firstTime);
d = getDateOfDaysAgo(daysSinceLastTime - firstTime);
setLong(key, d.getTime());
return firstTime < 1;
} else {
......
......@@ -12,10 +12,12 @@ package net.gsantner.opoc.ui;
import android.app.Activity;
import android.graphics.Typeface;
import android.os.AsyncTask;
import android.support.annotation.ColorInt;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.AppCompatEditText;
import android.text.Editable;
......@@ -24,6 +26,7 @@ import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ArrayAdapter;
import android.widget.Filter;
......@@ -31,12 +34,20 @@ import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import net.gsantner.opoc.util.ActivityUtils;
import net.gsantner.opoc.util.Callback;
import net.gsantner.opoc.util.ContextUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.IOFileFilter;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.regex.Pattern;
@SuppressWarnings("WeakerAccess")
public class SearchOrCustomTextDialog {
......@@ -115,6 +126,7 @@ public class SearchOrCustomTextDialog {
}
};
final ActivityUtils activityUtils = new ActivityUtils(activity);
final AppCompatEditText searchEditText = new AppCompatEditText(activity);
searchEditText.setSingleLine(true);
searchEditText.setMaxLines(1);
......@@ -140,6 +152,7 @@ public class SearchOrCustomTextDialog {
final ListView listView = new ListView(activity);
final LinearLayout linearLayout = new LinearLayout(activity);
listView.setAdapter(listAdapter);
listView.setVisibility(dopt.data != null && !dopt.data.isEmpty() ? View.VISIBLE : View.GONE);
linearLayout.setOrientation(LinearLayout.VERTICAL);
if (dopt.isSearchEnabled) {
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
......@@ -157,7 +170,7 @@ public class SearchOrCustomTextDialog {
dialogBuilder.setView(linearLayout)
.setTitle(dopt.titleText)
.setOnCancelListener(null)
.setNegativeButton(dopt.cancelButtonText, null);
.setNegativeButton(dopt.cancelButtonText, (dialogInterface, i) -> dialogInterface.dismiss());
if (dopt.isSearchEnabled) {
dialogBuilder.setPositiveButton(dopt.okButtonText, (dialogInterface, i) -> {
dialogInterface.dismiss();
......@@ -186,9 +199,124 @@ public class SearchOrCustomTextDialog {
return false;
});
if (dialog.getWindow() != null) {
dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
Window w;
if ((w = dialog.getWindow()) != null && dopt.isSearchEnabled) {
w.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
}
dialog.show();
if ((w = dialog.getWindow()) != null) {
w.setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT);
}
if (dopt.isSearchEnabled) {
searchEditText.requestFocus();
}
}
public static SearchFilesTask recursiveFileSearch(Activity activity, File searchDir, String query, Callback.a1<List<String>> callback) {
query = query.replaceAll("(?<![.])[*]", ".*");
SearchFilesTask task = new SearchFilesTask(activity, searchDir, query, callback, query.startsWith("^") || query.contains("*"));
task.execute();
return task;
}
public static class SearchFilesTask extends AsyncTask<Void, File, List<String>> implements IOFileFilter {
private final Callback.a1<List<String>> _callback;
private final File _searchDir;
private final String _query;
private final boolean _isRegex;
private final WeakReference<Activity> _activityRef;
private final Pattern _regex;
private Snackbar _snackBar;
public SearchFilesTask(Activity activity, File searchDir, String query, Callback.a1<List<String>> callback, boolean isRegex) {
_searchDir = searchDir;
_query = isRegex ? query : query.toLowerCase();
_callback = callback;
_isRegex = isRegex;
_regex = isRegex ? Pattern.compile(_query) : null;
_activityRef = new WeakReference<>(activity);
}
// Called for both, file and folder filter
@Override
public boolean accept(File file) {
return isMatching(file, true);
}
// Not called
@Override
public boolean accept(File dir, String name) {
return isMatching(new File(dir, name), true);
}
// In iterateFilesAndDirs, subdirs are only scanned when returning true on it
// But those dirs will also occur in iterator
// Hence call this aagain with alwaysMatchDir=false
public boolean isMatching(File file, boolean alwaysMatchDir) {
if (file.isDirectory()) {
// Do never scan .git directories, lots of files, lots of time
if (file.getName().equals(".git")) {
return false;
}
if (alwaysMatchDir) {
return true;
}
}
String name = file.getName();
file = file.getParentFile();
return _isRegex ? _regex.matcher(name).matches() : name.toLowerCase().contains(_query);
}
@Override
protected void onPreExecute() {
super.onPreExecute();
if (_activityRef.get() != null) {
_snackBar = Snackbar.make(_activityRef.get().findViewById(android.R.id.content), _query + "...", Snackbar.LENGTH_INDEFINITE);
_snackBar.setAction(android.R.string.cancel, (v) -> {
_snackBar.dismiss();
cancel(true);
}).show();
}
}
@Override
protected List<String> doInBackground(Void... voidp) {
List<String> ret = new ArrayList<>();
boolean first = true;
Iterator<File> iter = FileUtils.iterateFilesAndDirs(_searchDir, this, this);
while (iter.hasNext() && !isCancelled()) {
File f = iter.next();
if (first) {
first = false;
if (f.equals(_searchDir)) {
continue;
}
}
if (f.isFile() || (f.isDirectory() && isMatching(f, false))) {
ret.add(f.getAbsolutePath().replace(_searchDir.getAbsolutePath() + "/", ""));
}
}
return ret;
}
@Override
protected void onPostExecute(List<String> ret) {
super.onPostExecute(ret);
if (_snackBar != null) {
_snackBar.dismiss();
}
if (_callback != null) {
try {
_callback.callback(ret);
} catch (Exception ignored) {
}
}
new ActivityUtils(_activityRef.get()).hideSoftKeyboard().freeContextRef();
}
}
}
......@@ -17,8 +17,11 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.TypedArray;
import android.net.Uri;
import android.os.Build;
import android.provider.CalendarContract;
import android.support.annotation.ColorInt;
import android.support.annotation.StringRes;
import android.support.design.widget.Snackbar;
import android.support.v4.content.ContextCompat;
......@@ -47,6 +50,12 @@ public class ActivityUtils extends net.gsantner.opoc.util.ContextUtils {
_activity = activity;
}
@Override
public void freeContextRef() {
super.freeContextRef();
_activity = null;
}
//########################
//## Methods
//########################
......@@ -85,9 +94,11 @@ public class ActivityUtils extends net.gsantner.opoc.util.ContextUtils {
}
public void showSnackBar(@StringRes int stringResId, boolean showLong) {
Snackbar.make(_activity.findViewById(android.R.id.content), stringResId,
showLong ? Snackbar.LENGTH_LONG : Snackbar.LENGTH_SHORT).show();
public Snackbar showSnackBar(@StringRes int stringResId, boolean showLong) {
Snackbar s = Snackbar.make(_activity.findViewById(android.R.id.content), stringResId,
showLong ? Snackbar.LENGTH_LONG : Snackbar.LENGTH_SHORT);
s.show();
return s;
}
public void showSnackBar(@StringRes int stringResId, boolean showLong, @StringRes int actionResId, View.OnClickListener listener) {
......@@ -97,18 +108,58 @@ public class ActivityUtils extends net.gsantner.opoc.util.ContextUtils {
.show();
}
public void hideSoftKeyboard() {
InputMethodManager imm = (InputMethodManager) _activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
if (imm != null && _activity.getCurrentFocus() != null && _activity.getCurrentFocus().getWindowToken() != null) {
imm.hideSoftInputFromWindow(_activity.getCurrentFocus().getWindowToken(), 0);
public ActivityUtils setSoftKeyboardVisibile(boolean visible, View... editView) {
final Activity activity = _activity;
if (activity != null) {
final View v = (editView != null && editView.length > 0) ? (editView[0]) : (activity.getCurrentFocus() != null && activity.getCurrentFocus().getWindowToken() != null ? activity.getCurrentFocus() : null);
final InputMethodManager imm = (InputMethodManager) activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
if (v != null && imm != null) {
Runnable r = () -> {
if (visible) {
v.requestFocus();
imm.showSoftInput(v, InputMethodManager.SHOW_FORCED);
} else {
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
};
r.run();
for (int d : new int[]{100, 350}) {
v.postDelayed(r, d);
}
}
}
return this;
}
public void showSoftKeyboard() {
InputMethodManager imm = (InputMethodManager) _activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
if (imm != null && _activity.getCurrentFocus() != null && _activity.getCurrentFocus().getWindowToken() != null) {
imm.showSoftInput(_activity.getCurrentFocus(), InputMethodManager.SHOW_FORCED);
public ActivityUtils hideSoftKeyboard() {
if (_activity != null) {
InputMethodManager imm = (InputMethodManager) _activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
if (imm != null && _activity.getCurrentFocus() != null && _activity.getCurrentFocus().getWindowToken() != null) {
imm.hideSoftInputFromWindow(_activity.getCurrentFocus().getWindowToken(), 0);
}
}
return this;
}
public ActivityUtils showSoftKeyboard() {
if (_activity != null) {
InputMethodManager imm = (InputMethodManager) _activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
if (imm != null && _activity.getCurrentFocus() != null && _activity.getCurrentFocus().getWindowToken() != null) {
showSoftKeyboard(_activity.getCurrentFocus());
}
}
return this;
}
public ActivityUtils showSoftKeyboard(View textInputView) {
if (_activity != null) {
InputMethodManager imm = (InputMethodManager) _activity.getSystemService(Activity.INPUT_METHOD_SERVICE);
if (imm != null && textInputView != null) {
imm.showSoftInput(textInputView, InputMethodManager.SHOW_FORCED);
}
}
return this;
}
public void showDialogWithHtmlTextView(@StringRes int resTitleId, String html) {
......@@ -142,7 +193,7 @@ public class ActivityUtils extends net.gsantner.opoc.util.ContextUtils {
}
// Toggle with no param, else set visibility according to first bool
public void toggleStatusbarVisibility(boolean... optionalForceVisible) {
public ActivityUtils toggleStatusbarVisibility(boolean... optionalForceVisible) {
WindowManager.LayoutParams attrs = _activity.getWindow().getAttributes();
int flag = WindowManager.LayoutParams.FLAG_FULLSCREEN;
if (optionalForceVisible.length == 0) {
......@@ -153,9 +204,10 @@ public class ActivityUtils extends net.gsantner.opoc.util.ContextUtils {
attrs.flags |= flag;
}
_activity.getWindow().setAttributes(attrs);
return this;
}
public void showGooglePlayEntryForThisApp() {
public ActivityUtils showGooglePlayEntryForThisApp() {
String pkgId = "details?id=" + _activity.getPackageName();
Intent goToMarket = new Intent(Intent.ACTION_VIEW, Uri.parse("market://" + pkgId));
goToMarket.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY |
......@@ -167,9 +219,10 @@ public class ActivityUtils extends net.gsantner.opoc.util.ContextUtils {
_activity.startActivity(new Intent(Intent.ACTION_VIEW,
Uri.parse("https://play.google.com/store/apps/" + pkgId)));
}
return this;
}
public void setStatusbarColor(int color, boolean... fromRes) {
public ActivityUtils setStatusbarColor(int color, boolean... fromRes) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (fromRes != null && fromRes.length > 0 && fromRes[0]) {
color = ContextCompat.getColor(_context, color);
......@@ -177,13 +230,55 @@ public class ActivityUtils extends net.gsantner.opoc.util.ContextUtils {
_activity.getWindow().setStatusBarColor(color);
}
return this;
}
public void setLauncherActivityEnabled(Class activityClass, boolean enable) {
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);
pkg.setComponentEnabledSetting(component, enable ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
return this;
}
@ColorInt
public Integer getCurrentPrimaryColor() {
TypedValue typedValue = new TypedValue();
_context.getTheme().resolveAttribute(getResId(ResType.ATTR, "colorPrimary"), typedValue, true);
return typedValue.data;
}
@ColorInt
public Integer getCurrentPrimaryDarkColor() {
TypedValue typedValue = new TypedValue();
_context.getTheme().resolveAttribute(getResId(ResType.ATTR, "colorPrimaryDark"), typedValue, true);
return typedValue.data;
}
@ColorInt
public Integer getCurrentAccentColor() {
TypedValue typedValue = new TypedValue();
_context.getTheme().resolveAttribute(getResId(ResType.ATTR, "colorAccent"), typedValue, true);
return typedValue.data;
}
@ColorInt
public Integer getActivityBackgroundColor() {
TypedArray array = _activity.getTheme().obtainStyledAttributes(new int[]{
android.R.attr.colorBackground,
});
int c = array.getColor(0, 0xFF0000);
array.recycle();
return c;
}
public ActivityUtils startCalendarApp() {
Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon();
builder.appendPath("time");
builder.appendPath(Long.toString(System.currentTimeMillis()));
Intent intent = new Intent(Intent.ACTION_VIEW, builder.build());
_activity.startActivity(intent);
return this;
}
}
......@@ -31,4 +31,24 @@ public class Callback {
public interface a5<A, B, C, D, E> {
void callback(A arg1, B arg2, C arg3, D arg4, E arg5);
}
public interface b1<A> {
boolean callback(A arg1);
}
public interface b2<A, B> {
boolean callback(A arg1, B arg2);
}
public interface b3<A, B, C> {
boolean callback(A arg1, B arg2, C arg3);
}
public interface b4<A, B, C, D> {
boolean callback(A arg1, B arg2, C arg3, D arg4);
}
public interface b5<A, B, C, D, E> {
boolean callback(A arg1, B arg2, C arg3, D arg4, E arg5);
}
}
......@@ -38,6 +38,7 @@ import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.SystemClock;
import android.support.annotation.ColorInt;
import android.support.annotation.ColorRes;
......@@ -48,6 +49,7 @@ import android.support.annotation.StringRes;
import android.support.graphics.drawable.VectorDrawableCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v4.util.Pair;
import android.text.Html;
import android.text.InputFilter;
import android.text.SpannableString;
......@@ -74,6 +76,8 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
......@@ -94,6 +98,9 @@ public class ContextUtils {
return _context;
}
public void freeContextRef() {
_context = null;
}
//
// Class Methods
......@@ -171,15 +178,23 @@ public class ContextUtils {
return String.format(a ? "#%08X" : "#%06X", (a ? 0xFFFFFFFF : 0xFFFFFF) & intColor);
}
public String getAndroidVersion() {
return Build.VERSION.RELEASE + " (" + Build.VERSION.SDK_INT + ")";
}
public String getAppVersionName() {
PackageManager manager = _context.getPackageManager();
try {
PackageManager manager = _context.getPackageManager();
PackageInfo info = manager.getPackageInfo(getPackageIdManifest(), 0);
return info.versionName;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
return "?";
try {
PackageInfo info = manager.getPackageInfo(getPackageIdReal(), 0);
return info.versionName;
} catch (PackageManager.NameNotFoundException ignored) {
}
}
return "?";
}
public String getAppInstallationSource() {
......@@ -519,12 +534,76 @@ public class ContextUtils {
/**
* Get the private directory for the current package (usually /data/data/package.name/)
*/
public String getAppDataDir() {
@SuppressWarnings("StatementWithEmptyBody")
public File getAppDataPrivateDir() {
File filesDir;
try {
return _context.getPackageManager().getPackageInfo(getPackageIdReal(), 0).applicationInfo.dataDir;
filesDir = new File(new File(_context.getPackageManager().getPackageInfo(getPackageIdReal(), 0).applicationInfo.dataDir), "files");
} catch (PackageManager.NameNotFoundException e) {
return _context.getFilesDir().getParent();
filesDir = _context.getFilesDir();
}
if (!filesDir.exists() && filesDir.mkdirs()) ;
return filesDir;
}
/**
* Get public (accessible) appdata folders
*/
@SuppressWarnings("StatementWithEmptyBody")