Merge branch 'dev' of https://github.com/iSoron/uhabits into update_checkmark_widget
@ -0,0 +1,31 @@
|
|||||||
|
name: Build & Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: macOS-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
- name: Install Java Development Kit 1.8
|
||||||
|
uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: 1.8
|
||||||
|
- name: Build APK & Run small tests
|
||||||
|
run: android/build.sh build
|
||||||
|
- name: Run medium tests
|
||||||
|
uses: ReactiveCircus/android-emulator-runner@v2.2.0
|
||||||
|
with:
|
||||||
|
api-level: 29
|
||||||
|
script: android/build.sh medium-tests
|
||||||
|
- name: Upload artifacts
|
||||||
|
uses: actions/upload-artifact@v1
|
||||||
|
with:
|
||||||
|
name: Build
|
||||||
|
path: android/uhabits-android/build/outputs/
|
@ -0,0 +1,40 @@
|
|||||||
|
name: Build, Test & Publish
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: macOS-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v1
|
||||||
|
- name: Install GPG
|
||||||
|
uses: olafurpg/setup-gpg@v2
|
||||||
|
- name: Decrypt secrets
|
||||||
|
env:
|
||||||
|
GPG_PASSWORD: ${{ secrets.GPG_PASSWORD }}
|
||||||
|
run: .secret/decrypt.sh
|
||||||
|
- name: Install Java Development Kit 1.8
|
||||||
|
uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: 1.8
|
||||||
|
- name: Build APK & Run small tests
|
||||||
|
env:
|
||||||
|
RELEASE: 1
|
||||||
|
run: android/build.sh build
|
||||||
|
- name: Run medium tests
|
||||||
|
uses: ReactiveCircus/android-emulator-runner@v2.2.0
|
||||||
|
env:
|
||||||
|
RELEASE: 1
|
||||||
|
with:
|
||||||
|
api-level: 29
|
||||||
|
script: android/build.sh medium-tests
|
||||||
|
- name: Upload build to GitHub
|
||||||
|
uses: actions/upload-artifact@v1
|
||||||
|
with:
|
||||||
|
name: Build
|
||||||
|
path: android/uhabits-android/build/outputs/
|
||||||
|
- name: Upload APK to Google Play
|
||||||
|
run: cd android && ./gradlew publishReleaseApk
|
@ -0,0 +1,16 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
if [ -z "$GPG_PASSWORD" ]; then
|
||||||
|
echo Env variable GPG_PASSWORD must be defined
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
gpg \
|
||||||
|
--quiet \
|
||||||
|
--batch \
|
||||||
|
--yes \
|
||||||
|
--decrypt \
|
||||||
|
--passphrase="$GPG_PASSWORD" \
|
||||||
|
--output secret.tar.gz \
|
||||||
|
secret
|
||||||
|
tar -xzf secret.tar.gz
|
||||||
|
rm secret.tar.gz
|
@ -1,155 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.isoron.androidbase;
|
|
||||||
|
|
||||||
import android.content.*;
|
|
||||||
import android.os.*;
|
|
||||||
import android.support.annotation.*;
|
|
||||||
import android.view.*;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.text.*;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import javax.inject.*;
|
|
||||||
|
|
||||||
public class AndroidBugReporter
|
|
||||||
{
|
|
||||||
private final Context context;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public AndroidBugReporter(@NonNull @AppContext Context context)
|
|
||||||
{
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Captures and returns a bug report. The bug report contains some device
|
|
||||||
* information and the logcat.
|
|
||||||
*
|
|
||||||
* @return a String containing the bug report.
|
|
||||||
* @throws IOException when any I/O error occur.
|
|
||||||
*/
|
|
||||||
@NonNull
|
|
||||||
public String getBugReport() throws IOException
|
|
||||||
{
|
|
||||||
String logcat = getLogcat();
|
|
||||||
String deviceInfo = getDeviceInfo();
|
|
||||||
|
|
||||||
String log = "---------- BUG REPORT BEGINS ----------\n";
|
|
||||||
log += deviceInfo + "\n" + logcat;
|
|
||||||
log += "---------- BUG REPORT ENDS ------------\n";
|
|
||||||
|
|
||||||
return log;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDeviceInfo()
|
|
||||||
{
|
|
||||||
if (context == null) return "null context\n";
|
|
||||||
|
|
||||||
WindowManager wm =
|
|
||||||
(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
|
||||||
|
|
||||||
return
|
|
||||||
String.format("App Version Name: %s\n", BuildConfig.VERSION_NAME) +
|
|
||||||
String.format("App Version Code: %s\n", BuildConfig.VERSION_CODE) +
|
|
||||||
String.format("OS Version: %s (%s)\n",
|
|
||||||
System.getProperty("os.version"), Build.VERSION.INCREMENTAL) +
|
|
||||||
String.format("OS API Level: %s\n", Build.VERSION.SDK) +
|
|
||||||
String.format("Device: %s\n", Build.DEVICE) +
|
|
||||||
String.format("Model (Product): %s (%s)\n", Build.MODEL,
|
|
||||||
Build.PRODUCT) +
|
|
||||||
String.format("Manufacturer: %s\n", Build.MANUFACTURER) +
|
|
||||||
String.format("Other tags: %s\n", Build.TAGS) +
|
|
||||||
String.format("Screen Width: %s\n",
|
|
||||||
wm.getDefaultDisplay().getWidth()) +
|
|
||||||
String.format("Screen Height: %s\n",
|
|
||||||
wm.getDefaultDisplay().getHeight()) +
|
|
||||||
String.format("External storage state: %s\n\n",
|
|
||||||
Environment.getExternalStorageState());
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getLogcat() throws IOException
|
|
||||||
{
|
|
||||||
int maxLineCount = 250;
|
|
||||||
StringBuilder builder = new StringBuilder();
|
|
||||||
|
|
||||||
String[] command = new String[]{ "logcat", "-d" };
|
|
||||||
java.lang.Process process = Runtime.getRuntime().exec(command);
|
|
||||||
|
|
||||||
InputStreamReader in = new InputStreamReader(process.getInputStream());
|
|
||||||
BufferedReader bufferedReader = new BufferedReader(in);
|
|
||||||
|
|
||||||
LinkedList<String> log = new LinkedList<>();
|
|
||||||
|
|
||||||
String line;
|
|
||||||
while ((line = bufferedReader.readLine()) != null)
|
|
||||||
{
|
|
||||||
log.addLast(line);
|
|
||||||
if (log.size() > maxLineCount) log.removeFirst();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (String l : log)
|
|
||||||
{
|
|
||||||
builder.append(l);
|
|
||||||
builder.append('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Captures a bug report and saves it to a file in the SD card.
|
|
||||||
* <p>
|
|
||||||
* The contents of the file are generated by the method {@link
|
|
||||||
* #getBugReport()}. The file is saved in the apps's external private
|
|
||||||
* storage.
|
|
||||||
*
|
|
||||||
* @return the generated file.
|
|
||||||
* @throws IOException when I/O errors occur.
|
|
||||||
*/
|
|
||||||
@NonNull
|
|
||||||
public void dumpBugReportToFile()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
|
|
||||||
String date =
|
|
||||||
new SimpleDateFormat("yyyy-MM-dd HHmmss", Locale.US).format(
|
|
||||||
new Date());
|
|
||||||
|
|
||||||
if (context == null) throw new IllegalStateException();
|
|
||||||
|
|
||||||
File dir = new AndroidDirFinder(context).getFilesDir("Logs");
|
|
||||||
if (dir == null)
|
|
||||||
throw new IOException("log dir should not be null");
|
|
||||||
|
|
||||||
File logFile =
|
|
||||||
new File(String.format("%s/Log %s.txt", dir.getPath(), date));
|
|
||||||
FileWriter output = new FileWriter(logFile);
|
|
||||||
output.write(getBugReport());
|
|
||||||
output.close();
|
|
||||||
}
|
|
||||||
catch (IOException e)
|
|
||||||
{
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,110 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.isoron.androidbase
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.Environment
|
||||||
|
import android.view.WindowManager
|
||||||
|
import java.io.*
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
open class AndroidBugReporter @Inject constructor(@AppContext private val context: Context) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Captures and returns a bug report. The bug report contains some device
|
||||||
|
* information and the logcat.
|
||||||
|
*
|
||||||
|
* @return a String containing the bug report.
|
||||||
|
* @throws IOException when any I/O error occur.
|
||||||
|
*/
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun getBugReport(): String {
|
||||||
|
var log = "---------- BUG REPORT BEGINS ----------\n"
|
||||||
|
log += "${getLogcat()}\n"
|
||||||
|
log += "${getDeviceInfo()}\n"
|
||||||
|
log += "---------- BUG REPORT ENDS ------------\n"
|
||||||
|
return log
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun getLogcat(): String {
|
||||||
|
val maxLineCount = 250
|
||||||
|
val builder = StringBuilder()
|
||||||
|
val process = Runtime.getRuntime().exec(arrayOf("logcat", "-d"))
|
||||||
|
val inputReader = InputStreamReader(process.inputStream)
|
||||||
|
val bufferedReader = BufferedReader(inputReader)
|
||||||
|
val log = LinkedList<String>()
|
||||||
|
var line: String?
|
||||||
|
while (true) {
|
||||||
|
line = bufferedReader.readLine()
|
||||||
|
if (line == null) break;
|
||||||
|
log.addLast(line)
|
||||||
|
if (log.size > maxLineCount) log.removeFirst()
|
||||||
|
}
|
||||||
|
for (l in log) {
|
||||||
|
builder.appendln(l)
|
||||||
|
}
|
||||||
|
return builder.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Captures a bug report and saves it to a file in the SD card.
|
||||||
|
*
|
||||||
|
* The contents of the file are generated by the method [ ][.getBugReport]. The file is saved
|
||||||
|
* in the apps's external private storage.
|
||||||
|
*
|
||||||
|
* @return the generated file.
|
||||||
|
* @throws IOException when I/O errors occur.
|
||||||
|
*/
|
||||||
|
fun dumpBugReportToFile() {
|
||||||
|
try {
|
||||||
|
val date = SimpleDateFormat("yyyy-MM-dd HHmmss", Locale.US).format(Date())
|
||||||
|
val dir = AndroidDirFinder(context).getFilesDir("Logs")
|
||||||
|
?: throw IOException("log dir should not be null")
|
||||||
|
val logFile = File(String.format("%s/Log %s.txt", dir.path, date))
|
||||||
|
val output = FileWriter(logFile)
|
||||||
|
output.write(getBugReport())
|
||||||
|
output.close()
|
||||||
|
} catch (e: IOException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getDeviceInfo(): String {
|
||||||
|
val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
||||||
|
return buildString {
|
||||||
|
appendln("App Version Name: ${BuildConfig.VERSION_NAME}")
|
||||||
|
appendln("App Version Code: ${BuildConfig.VERSION_CODE}")
|
||||||
|
appendln("OS Version: ${System.getProperty("os.version")} (${Build.VERSION.INCREMENTAL})")
|
||||||
|
appendln("OS API Level: ${Build.VERSION.SDK}")
|
||||||
|
appendln("Device: ${Build.DEVICE}")
|
||||||
|
appendln("Model (Product): ${Build.MODEL} (${Build.PRODUCT})")
|
||||||
|
appendln("Manufacturer: ${Build.MANUFACTURER}")
|
||||||
|
appendln("Other tags: ${Build.TAGS}")
|
||||||
|
appendln("Screen Width: ${wm.defaultDisplay.width}")
|
||||||
|
appendln("Screen Height: ${wm.defaultDisplay.height}")
|
||||||
|
appendln("External storage state: ${Environment.getExternalStorageState()}")
|
||||||
|
appendln()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.isoron.androidbase;
|
|
||||||
|
|
||||||
import android.content.*;
|
|
||||||
import android.support.annotation.*;
|
|
||||||
import android.support.v4.content.*;
|
|
||||||
import android.util.*;
|
|
||||||
|
|
||||||
import org.isoron.androidbase.utils.*;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
|
|
||||||
import javax.inject.*;
|
|
||||||
|
|
||||||
public class AndroidDirFinder
|
|
||||||
{
|
|
||||||
@NonNull
|
|
||||||
private Context context;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public AndroidDirFinder(@NonNull @AppContext Context context)
|
|
||||||
{
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public File getFilesDir(@Nullable String relativePath)
|
|
||||||
{
|
|
||||||
File externalFilesDirs[] =
|
|
||||||
ContextCompat.getExternalFilesDirs(context, null);
|
|
||||||
if (externalFilesDirs == null)
|
|
||||||
{
|
|
||||||
Log.e("BaseSystem",
|
|
||||||
"getFilesDir: getExternalFilesDirs returned null");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return FileUtils.getDir(externalFilesDirs, relativePath);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.isoron.androidbase
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import org.isoron.androidbase.utils.FileUtils
|
||||||
|
import java.io.File
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
class AndroidDirFinder @Inject constructor(@param:AppContext private val context: Context) {
|
||||||
|
fun getFilesDir(relativePath: String?): File? {
|
||||||
|
return FileUtils.getDir(
|
||||||
|
ContextCompat.getExternalFilesDirs(context, null),
|
||||||
|
relativePath
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -1,67 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.isoron.androidbase;
|
|
||||||
|
|
||||||
import android.support.annotation.*;
|
|
||||||
|
|
||||||
import org.isoron.androidbase.activities.*;
|
|
||||||
|
|
||||||
public class BaseExceptionHandler implements Thread.UncaughtExceptionHandler
|
|
||||||
{
|
|
||||||
@Nullable
|
|
||||||
private Thread.UncaughtExceptionHandler originalHandler;
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private BaseActivity activity;
|
|
||||||
|
|
||||||
public BaseExceptionHandler(@NonNull BaseActivity activity)
|
|
||||||
{
|
|
||||||
this.activity = activity;
|
|
||||||
originalHandler = Thread.getDefaultUncaughtExceptionHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void uncaughtException(@Nullable Thread thread,
|
|
||||||
@Nullable Throwable ex)
|
|
||||||
{
|
|
||||||
if (ex == null) return;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ex.printStackTrace();
|
|
||||||
new AndroidBugReporter(activity).dumpBugReportToFile();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (ex.getCause() instanceof InconsistentDatabaseException)
|
|
||||||
// {
|
|
||||||
// HabitsApplication app = (HabitsApplication) activity.getApplication();
|
|
||||||
// HabitList habits = app.getComponent().getHabitList();
|
|
||||||
// habits.repair();
|
|
||||||
// System.exit(0);
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (originalHandler != null)
|
|
||||||
originalHandler.uncaughtException(thread, ex);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.isoron.androidbase
|
||||||
|
|
||||||
|
import org.isoron.androidbase.activities.BaseActivity
|
||||||
|
|
||||||
|
class BaseExceptionHandler(private val activity: BaseActivity) : Thread.UncaughtExceptionHandler {
|
||||||
|
|
||||||
|
private val originalHandler: Thread.UncaughtExceptionHandler? =
|
||||||
|
Thread.getDefaultUncaughtExceptionHandler()
|
||||||
|
|
||||||
|
override fun uncaughtException(thread: Thread?, ex: Throwable?) {
|
||||||
|
if (ex == null) return
|
||||||
|
if (thread == null) return
|
||||||
|
try {
|
||||||
|
ex.printStackTrace()
|
||||||
|
AndroidBugReporter(activity).dumpBugReportToFile()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
originalHandler?.uncaughtException(thread, ex)
|
||||||
|
}
|
||||||
|
}
|
@ -1,71 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.isoron.androidbase;
|
|
||||||
|
|
||||||
import android.content.*;
|
|
||||||
import android.support.annotation.*;
|
|
||||||
|
|
||||||
import org.isoron.androidbase.*;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.security.*;
|
|
||||||
import java.security.cert.Certificate;
|
|
||||||
import java.security.cert.*;
|
|
||||||
|
|
||||||
import javax.inject.*;
|
|
||||||
import javax.net.ssl.*;
|
|
||||||
|
|
||||||
public class SSLContextProvider
|
|
||||||
{
|
|
||||||
private Context context;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public SSLContextProvider(@NonNull @AppContext Context context)
|
|
||||||
{
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SSLContext getCACertSSLContext()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
|
||||||
InputStream caInput = context.getAssets().open("cacert.pem");
|
|
||||||
Certificate ca = cf.generateCertificate(caInput);
|
|
||||||
|
|
||||||
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
|
|
||||||
ks.load(null, null);
|
|
||||||
ks.setCertificateEntry("ca", ca);
|
|
||||||
|
|
||||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance(
|
|
||||||
TrustManagerFactory.getDefaultAlgorithm());
|
|
||||||
tmf.init(ks);
|
|
||||||
|
|
||||||
SSLContext ctx = SSLContext.getInstance("TLS");
|
|
||||||
ctx.init(null, tmf.getTrustManagers(), null);
|
|
||||||
|
|
||||||
return ctx;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.isoron.androidbase
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import java.security.KeyStore
|
||||||
|
import java.security.cert.CertificateFactory
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.net.ssl.SSLContext
|
||||||
|
import javax.net.ssl.TrustManagerFactory
|
||||||
|
|
||||||
|
class SSLContextProvider @Inject constructor(@param:AppContext private val context: Context) {
|
||||||
|
fun getCACertSSLContext(): SSLContext {
|
||||||
|
try {
|
||||||
|
val cf = CertificateFactory.getInstance("X.509")
|
||||||
|
val ca = cf.generateCertificate(context.assets.open("cacert.pem"))
|
||||||
|
val ks = KeyStore.getInstance(KeyStore.getDefaultType()).apply {
|
||||||
|
load(null, null)
|
||||||
|
setCertificateEntry("ca", ca)
|
||||||
|
}
|
||||||
|
val alg = TrustManagerFactory.getDefaultAlgorithm()
|
||||||
|
val tmf = TrustManagerFactory.getInstance(alg).apply {
|
||||||
|
init(ks)
|
||||||
|
}
|
||||||
|
return SSLContext.getInstance("TLS").apply {
|
||||||
|
init(null, tmf.trustManagers, null)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
throw RuntimeException(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,18 @@
|
|||||||
VERSION_CODE = 35
|
VERSION_CODE = 51
|
||||||
VERSION_NAME = 1.7.8
|
VERSION_NAME = 1.8.8
|
||||||
|
|
||||||
MIN_SDK_VERSION = 19
|
MIN_SDK_VERSION = 21
|
||||||
TARGET_SDK_VERSION = 27
|
TARGET_SDK_VERSION = 29
|
||||||
COMPILE_SDK_VERSION = 27
|
COMPILE_SDK_VERSION = 29
|
||||||
|
|
||||||
DAGGER_VERSION = 2.9
|
DAGGER_VERSION = 2.25.4
|
||||||
BUILD_TOOLS_VERSION = 27.0.3
|
KOTLIN_VERSION = 1.3.61
|
||||||
KOTLIN_VERSION = 1.2.41
|
SUPPORT_LIBRARY_VERSION = 28.0.0
|
||||||
SUPPORT_LIBRARY_VERSION = 27.1.1
|
AUTO_FACTORY_VERSION = 1.0-beta6
|
||||||
|
BUILD_TOOLS_VERSION = 4.0.0
|
||||||
|
|
||||||
org.gradle.parallel=false
|
org.gradle.parallel=false
|
||||||
org.gradle.daemon=true
|
org.gradle.daemon=true
|
||||||
org.gradle.jvmargs=-Xms2048m -Xmx2048m -XX:MaxPermSize=2048m
|
org.gradle.jvmargs=-Xms2048m -Xmx2048m -XX:MaxPermSize=2048m
|
||||||
|
android.useAndroidX=true
|
||||||
|
android.enableJetifier=true
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#Sun Sep 24 06:01:27 CDT 2017
|
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
uhabits-android/src/main/play/
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 32 KiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 57 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 31 KiB |
@ -1,65 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.isoron.uhabits;
|
|
||||||
|
|
||||||
import android.os.*;
|
|
||||||
import android.support.test.runner.*;
|
|
||||||
import android.test.suitebuilder.annotation.*;
|
|
||||||
|
|
||||||
import org.isoron.androidbase.*;
|
|
||||||
import org.isoron.uhabits.core.models.*;
|
|
||||||
import org.junit.*;
|
|
||||||
import org.junit.runner.*;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.*;
|
|
||||||
import static org.hamcrest.CoreMatchers.*;
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
@MediumTest
|
|
||||||
public class HabitLoggerTest extends BaseAndroidTest
|
|
||||||
{
|
|
||||||
@Test
|
|
||||||
public void testLogReminderScheduled() throws IOException
|
|
||||||
{
|
|
||||||
if (!isLogcatAvailable()) return;
|
|
||||||
|
|
||||||
long time = 1422277200000L; // 13:00 jan 26, 2015 (UTC)
|
|
||||||
Habit habit = fixtures.createEmptyHabit();
|
|
||||||
habit.setName("Write journal");
|
|
||||||
|
|
||||||
logger.logReminderScheduled(habit, time);
|
|
||||||
|
|
||||||
String expectedMsg = "Setting alarm (2015-01-26 130000): Wri\n";
|
|
||||||
assertLogcatContains(expectedMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void assertLogcatContains(String expectedMsg) throws IOException
|
|
||||||
{
|
|
||||||
String logcat = new AndroidBugReporter(targetContext).getLogcat();
|
|
||||||
assertThat(logcat, containsString(expectedMsg));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected boolean isLogcatAvailable()
|
|
||||||
{
|
|
||||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.activities.habits.show.views;
|
||||||
|
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import androidx.test.filters.MediumTest;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.BaseViewTest;
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
|
import org.isoron.uhabits.core.models.Habit;
|
||||||
|
import org.isoron.uhabits.core.models.Reminder;
|
||||||
|
import org.isoron.uhabits.core.models.WeekdayList;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@MediumTest
|
||||||
|
public class NotesCardTest extends BaseViewTest
|
||||||
|
{
|
||||||
|
public static final String PATH = "habits/show/NotesCard/";
|
||||||
|
|
||||||
|
private NotesCard view;
|
||||||
|
|
||||||
|
private Habit habit;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
@Override
|
||||||
|
public void setUp()
|
||||||
|
{
|
||||||
|
super.setUp();
|
||||||
|
|
||||||
|
habit = fixtures.createLongHabit();
|
||||||
|
habit.setReminder(new Reminder(8, 30, WeekdayList.EVERY_DAY));
|
||||||
|
|
||||||
|
view = LayoutInflater
|
||||||
|
.from(targetContext)
|
||||||
|
.inflate(R.layout.show_habit, null)
|
||||||
|
.findViewById(R.id.notesCard);
|
||||||
|
|
||||||
|
view.setHabit(habit);
|
||||||
|
view.refreshData();
|
||||||
|
|
||||||
|
measureView(view, 800, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRender() throws Exception
|
||||||
|
{
|
||||||
|
assertRenders(view, PATH + "render.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRenderEmptyDescription() throws Exception
|
||||||
|
{
|
||||||
|
habit.setDescription("");
|
||||||
|
view.refreshData();
|
||||||
|
assertRenders(view, PATH + "render-empty-description.png");
|
||||||
|
}
|
||||||
|
}
|
@ -1,51 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.isoron.uhabits.integration;
|
|
||||||
|
|
||||||
import android.support.test.filters.*;
|
|
||||||
import android.support.test.runner.*;
|
|
||||||
|
|
||||||
import org.isoron.uhabits.*;
|
|
||||||
import org.isoron.uhabits.activities.about.*;
|
|
||||||
import org.isoron.uhabits.activities.habits.list.*;
|
|
||||||
import org.junit.*;
|
|
||||||
import org.junit.runner.*;
|
|
||||||
|
|
||||||
import static java.lang.Thread.*;
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
@LargeTest
|
|
||||||
public class SavedStateTest extends BaseUserInterfaceTest
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Make sure that the main activity can be recreated by using
|
|
||||||
* BundleSavedState after being destroyed. See bug:
|
|
||||||
* https://github.com/iSoron/uhabits/issues/287
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testBundleSavedState() throws Exception
|
|
||||||
{
|
|
||||||
startActivity(ListHabitsActivity.class);
|
|
||||||
device.waitForIdle();
|
|
||||||
startActivity(AboutActivity.class);
|
|
||||||
sleep(1000);
|
|
||||||
device.pressBack();
|
|
||||||
}
|
|
||||||
}
|
|