resoved conflict

pull/316/head
Wei Lun 8 years ago
commit b26e7deba2

3
.gitignore vendored

@ -3,6 +3,7 @@
*.class
*.dex
*.iml
*.local
*.local.*
*.swp
*.trace
@ -22,3 +23,5 @@ gen/
local.properties
crowdin.yaml
local
tmp/
secret/

@ -1,13 +1,17 @@
# Loop Habit Tracker
<a href="https://circleci.com/gh/iSoron/uhabits/tree/dev">
<img src="https://img.shields.io/circleci/project/iSoron/uhabits/dev.svg">
<a href="https://build.loophabits.org/project.html?projectId=LoopHabitTracker&tab=projectOverview&guest=1">
<img src="https://img.shields.io/teamcity/https/build.loophabits.org/s/release.svg">
</a>
<!--
<a href="https://codecov.io/github/iSoron/uhabits?branch=dev">
<img src="https://img.shields.io/codecov/c/github/iSoron/uhabits.svg" alt="Coverage via Codecov" />
<a href="https://build.loophabits.org/project.html?projectId=LoopHabitTracker&tab=preport_project1_Coverage__core_&guest=1">
<img src="https://build.loophabits.org/app/rest/builds/buildType(id:release)/artifacts/content/coverage-badge.svg?guest=1" />
</a>
<a href="https://github.com/iSoron/uhabits/releases/latest">
<img src="https://img.shields.io/badge/apk-stable-FF4081.svg" />
</a>
<a href="https://build.loophabits.org/app/rest/builds/buildType(id:release)/artifacts/content/loop-dev.apk?guest=1">
<img src="https://img.shields.io/badge/apk-nightly-FF4081.svg" />
</a>
-->
Loop is a simple Android app that helps you create and maintain good habits,
allowing you to achieve your long-term goals. Detailed graphs and statistics
@ -15,8 +19,8 @@ show you how your habits improved over time. It is completely ad-free and open
source.
<p align="center">
<a href="https://play.google.com/store/apps/details?id=org.isoron.uhabits&utm_source=global_co&utm_medium=prtnr&utm_content=Mar2515&utm_campaign=PartBadge&pcampaignid=MKT-AC-global-none-all-co-pr-py-PartBadges-Oct1515-1"><img alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/images/apps/en-play-badge-border.png" height="75px"/></a>
<a href="http://f-droid.org/app/org.isoron.uhabits"><img alt="Git if on F-Droid" src="http://i.imgur.com/baSPE7X.png" height="75px"/></a>
<a href="https://play.google.com/store/apps/details?id=org.isoron.uhabits&utm_source=global_co&utm_medium=prtnr&utm_content=Mar2515&utm_campaign=PartBadge&pcampaignid=MKT-AC-global-none-all-co-pr-py-PartBadges-Oct1515-1"><img alt="Get it on Google Play" src="https://play.google.com/intl/en_us/badges/images/apps/en-play-badge-border.png" height="75px"/></a>
<a href="http://f-droid.org/app/org.isoron.uhabits"><img alt="Git if on F-Droid" src="http://i.imgur.com/baSPE7X.png" height="75px"/></a>
</p>
## Screenshots
@ -84,7 +88,7 @@ contribute, even if you are not a software developer.
* **Translate the app into your own language.** If you are not a native English
speaker, and would like to see the app translated into your own language,
please join our [open translation project at POEditor][poedit]. If the translation
please join our [open translation project][poedit]. If the translation
is already completed, you are also very welcome to join and proofread it.
* **Write some code.** If you are an Android developer, you are very welcome to
@ -121,7 +125,7 @@ contribute, even if you are not a software developer.
[screen4th]: screenshots/thumbs/uhabits4.png
[screen5th]: screenshots/thumbs/uhabits5.png
[screen6th]: screenshots/thumbs/uhabits6.png
[poedit]: https://poeditor.com/join/project/8DWX5pfjS0
[poedit]: http://translate.loophabits.org
[playstore]: https://play.google.com/store/apps/details?id=org.isoron.uhabits
[releases]: https://github.com/iSoron/uhabits/releases
[fdroid]: http://f-droid.org/app/org.isoron.uhabits

@ -0,0 +1 @@
/build

@ -0,0 +1,46 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion COMPILE_SDK_VERSION as Integer
buildToolsVersion BUILD_TOOLS_VERSION
defaultConfig {
minSdkVersion MIN_SDK_VERSION as Integer
targetSdkVersion TARGET_SDK_VERSION as Integer
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation "com.google.dagger:dagger:$DAGGER_VERSION"
implementation "com.android.support:design:$SUPPORT_LIBRARY_VERSION"
implementation "com.android.support:appcompat-v7:$SUPPORT_LIBRARY_VERSION"
implementation "org.apache.commons:commons-lang3:3.5"
annotationProcessor "com.google.dagger:dagger-compiler:$DAGGER_VERSION"
androidTestAnnotationProcessor "com.google.dagger:dagger-compiler:$DAGGER_VERSION"
androidTestImplementation "com.google.dagger:dagger:$DAGGER_VERSION"
testAnnotationProcessor "com.google.dagger:dagger-compiler:$DAGGER_VERSION"
testImplementation "junit:junit:4.12"
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
}

@ -0,0 +1,25 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /gemini-b/opt/android-sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

@ -0,0 +1,2 @@
<manifest package="org.isoron.androidbase"
xmlns:android="http://schemas.android.com/apk/res/android"/>

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
@ -17,75 +17,32 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.activities;
package org.isoron.androidbase;
import android.content.*;
import android.os.*;
import android.support.annotation.*;
import android.view.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.utils.*;
import java.io.*;
import java.lang.Process;
import java.text.*;
import java.util.*;
import javax.inject.*;
/**
* Base class for all systems class in the application.
* <p>
* Classes derived from BaseSystem are responsible for handling events and
* sending requests to the Android operating system. Examples include capturing
* a bug report, obtaining device information, or requesting runtime
* permissions.
*/
@ActivityScope
public class BaseSystem
public class AndroidBugReporter
{
private Context context;
private final Context context;
@Inject
public BaseSystem(@ActivityContext Context context)
public AndroidBugReporter(@NonNull @AppContext Context context)
{
this.context = context;
}
/**
* 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 File dumpBugReportToFile() throws IOException
{
String date =
DateFormats.getBackupDateFormat().format(DateUtils.getLocalTime());
if (context == null) throw new RuntimeException(
"application context should not be null");
File dir = FileUtils.getFilesDir(context, "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();
return logFile;
}
/**
* Captures and returns a bug report.
* <p>
* The bug report contains some device information and the logcat.
* 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.
@ -103,13 +60,39 @@ public class BaseSystem
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" };
Process process = Runtime.getRuntime().exec(command);
java.lang.Process process = Runtime.getRuntime().exec(command);
InputStreamReader in = new InputStreamReader(process.getInputStream());
BufferedReader bufferedReader = new BufferedReader(in);
@ -132,29 +115,41 @@ public class BaseSystem
return builder.toString();
}
private String getDeviceInfo()
/**
* 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()
{
if (context == null) return "null context\n";
try
{
WindowManager wm =
(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
String date =
new SimpleDateFormat("yyyy-MM-dd HHmmss", Locale.US).format(
new Date());
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());
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();
}
}
}

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
@ -17,38 +17,42 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits;
package org.isoron.androidbase;
import android.content.*;
import android.support.annotation.*;
import android.support.v4.content.*;
import android.util.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.utils.*;
import org.isoron.androidbase.utils.*;
import java.text.*;
import java.util.*;
import java.io.*;
import javax.inject.*;
@AppScope
public class HabitLogger
public class AndroidDirFinder
{
@NonNull
private Context context;
@Inject
public HabitLogger()
public AndroidDirFinder(@NonNull @AppContext Context context)
{
this.context = context;
}
public void logReminderScheduled(@NonNull Habit habit,
@NonNull Long reminderTime)
@Nullable
public File getFilesDir(@Nullable String relativePath)
{
int min = Math.min(3, habit.getName().length());
String name = habit.getName().substring(0, min);
DateFormat df = DateFormats.getBackupDateFormat();
String time = df.format(new Date(reminderTime));
Log.i("ReminderHelper",
String.format("Setting alarm (%s): %s", time, name));
File externalFilesDirs[] =
ContextCompat.getExternalFilesDirs(context, null);
if (externalFilesDirs == null)
{
Log.e("BaseSystem",
"getFilesDir: getExternalFilesDirs returned null");
return null;
}
return FileUtils.getDir(externalFilesDirs, relativePath);
}
}

@ -17,7 +17,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits;
package org.isoron.androidbase;
import java.lang.annotation.*;

@ -17,18 +17,18 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits;
package org.isoron.androidbase;
import android.content.*;
import dagger.*;
@Module
public class AppModule
public class AppContextModule
{
private final Context context;
public AppModule(@AppContext Context context)
public AppContextModule(@AppContext Context context)
{
this.context = context;
}

@ -0,0 +1,67 @@
/*
* 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,71 @@
/*
* 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);
}
}
}

@ -17,7 +17,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.activities;
package org.isoron.androidbase.activities;
import java.lang.annotation.*;

@ -17,20 +17,26 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits;
package org.isoron.androidbase.activities;
import org.isoron.uhabits.models.sqlite.*;
import org.isoron.uhabits.tasks.*;
import android.content.*;
import dagger.*;
@AppScope
@Component(modules = {
AppModule.class, SingleThreadTaskRunner.class, SQLModelFactory.class
})
public interface AndroidTestComponent extends AppComponent
@Module
public class ActivityContextModule
{
private Context context;
public ActivityContextModule(Context context)
{
this.context = context;
}
@Provides
@ActivityContext
public Context getContext()
{
return context;
}
}

@ -17,7 +17,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.activities;
package org.isoron.androidbase.activities;
import javax.inject.*;

@ -17,7 +17,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.activities;
package org.isoron.androidbase.activities;
import android.content.*;
import android.os.*;
@ -25,12 +25,10 @@ import android.support.annotation.*;
import android.support.v7.app.*;
import android.view.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.activities.habits.list.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.models.sqlite.*;
import org.isoron.androidbase.*;
import static android.R.anim.*;
import static android.R.anim.fade_in;
import static android.R.anim.fade_out;
/**
* Base class for all activities in the application.
@ -41,28 +39,19 @@ import static android.R.anim.*;
* {@link BaseScreen}.
* <p>
* A BaseActivity also installs an {@link java.lang.Thread.UncaughtExceptionHandler}
* to the main thread that logs the exception to the disk before the application
* crashes.
* to the main thread. By default, this handler is an instance of
* BaseExceptionHandler, which logs the exception to the disk before the application
* crashes. To the default handler, you should override the method
* getExceptionHandler.
*/
abstract public class BaseActivity extends AppCompatActivity
implements Thread.UncaughtExceptionHandler
{
@Nullable
private BaseMenu baseMenu;
@Nullable
private Thread.UncaughtExceptionHandler androidExceptionHandler;
@Nullable
private BaseScreen screen;
private ActivityComponent component;
public ActivityComponent getComponent()
{
return component;
}
@Override
public boolean onCreateOptionsMenu(@Nullable Menu menu)
{
@ -80,13 +69,13 @@ abstract public class BaseActivity extends AppCompatActivity
return baseMenu.onItemSelected(item);
}
public void restartWithFade()
public void restartWithFade(Class<?> cls)
{
new Handler().postDelayed(() -> {
Intent intent = new Intent(this, ListHabitsActivity.class);
new Handler().postDelayed(() ->
{
finish();
overridePendingTransition(fade_in, fade_out);
startActivity(intent);
startActivity(new Intent(this, cls));
}, 500); // HACK: Let the menu disappear first
}
@ -111,35 +100,6 @@ abstract public class BaseActivity extends AppCompatActivity
dialog.show();
}
@Override
public void uncaughtException(@Nullable Thread thread,
@Nullable Throwable ex)
{
if (ex == null) return;
try
{
ex.printStackTrace();
new BaseSystem(this).dumpBugReportToFile();
}
catch (Exception e)
{
// ignored
}
if (ex.getCause() instanceof InconsistentDatabaseException)
{
HabitsApplication app = (HabitsApplication) getApplication();
HabitList habits = app.getComponent().getHabitList();
habits.repair();
System.exit(0);
}
if (androidExceptionHandler != null)
androidExceptionHandler.uncaughtException(thread, ex);
else System.exit(1);
}
@Override
protected void onActivityResult(int request, int result, Intent data)
{
@ -151,18 +111,18 @@ abstract public class BaseActivity extends AppCompatActivity
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Thread.setDefaultUncaughtExceptionHandler(getExceptionHandler());
}
androidExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
HabitsApplication app = (HabitsApplication) getApplicationContext();
component = DaggerActivityComponent
.builder()
.activityModule(new ActivityModule(this))
.appComponent(app.getComponent())
.build();
protected Thread.UncaughtExceptionHandler getExceptionHandler()
{
return new BaseExceptionHandler(this);
}
component.getThemeSwitcher().apply();
@Override
protected void onResume()
{
super.onResume();
if(screen != null) screen.reattachDialogs();
}
}

@ -17,31 +17,22 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.activities;
import android.content.*;
package org.isoron.androidbase.activities;
import dagger.*;
@Module
public class ActivityModule
public class BaseActivityModule
{
private BaseActivity activity;
public ActivityModule(BaseActivity activity)
public BaseActivityModule(BaseActivity activity)
{
this.activity = activity;
}
@Provides
public BaseActivity getActivity()
{
return activity;
}
@Provides
@ActivityContext
public Context getContext()
public BaseActivity getBaseActivity()
{
return activity;
}

@ -17,13 +17,11 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.activities;
package org.isoron.androidbase.activities;
import android.support.annotation.*;
import android.view.*;
import javax.annotation.*;
/**
* Base class for all the menus in the application.
* <p>
@ -41,6 +39,12 @@ public abstract class BaseMenu
this.activity = activity;
}
@NonNull
public BaseActivity getActivity()
{
return activity;
}
/**
* Declare that the menu has changed, and should be recreated.
*/
@ -64,14 +68,13 @@ public abstract class BaseMenu
/**
* Called when the menu is first displayed.
* <p>
* This method cannot be overridden. The application should override the
* methods onCreate(Menu) and getMenuResourceId instead.
* This method should not be overridden. The application should override
* the methods onCreate(Menu) and getMenuResourceId instead.
*
* @param inflater a menu inflater, for creating the menu
* @param menu the menu that is being created.
*/
public final void onCreate(@NonNull MenuInflater inflater,
@NonNull Menu menu)
public void onCreate(@NonNull MenuInflater inflater, @NonNull Menu menu)
{
menu.clear();
inflater.inflate(getMenuResourceId(), menu);
@ -94,6 +97,6 @@ public abstract class BaseMenu
*
* @return id of the menu resource.
*/
@Resource
@MenuRes
protected abstract int getMenuResourceId();
}

@ -17,20 +17,19 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.activities;
package org.isoron.androidbase.activities;
import android.content.*;
import android.support.annotation.*;
import android.support.v4.content.res.*;
import android.support.v7.widget.Toolbar;
import android.view.*;
import android.widget.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.utils.*;
import org.isoron.androidbase.*;
import org.isoron.androidbase.utils.*;
import static android.os.Build.VERSION.*;
import static android.os.Build.VERSION_CODES.*;
import static android.os.Build.VERSION.SDK_INT;
import static android.os.Build.VERSION_CODES.LOLLIPOP;
/**
* Base class for all root views in the application.
@ -42,36 +41,42 @@ import static android.os.Build.VERSION_CODES.*;
*/
public abstract class BaseRootView extends FrameLayout
{
@NonNull
private final Context context;
private final BaseActivity activity;
protected boolean shouldDisplayHomeAsUp = false;
private final ThemeSwitcher themeSwitcher;
@Nullable
private BaseScreen screen;
public BaseRootView(Context context)
public BaseRootView(@NonNull Context context)
{
super(context);
this.context = context;
activity = (BaseActivity) context;
themeSwitcher = activity.getComponent().getThemeSwitcher();
}
public boolean getDisplayHomeAsUp()
{
return false;
return shouldDisplayHomeAsUp;
}
public void setDisplayHomeAsUp(boolean b)
{
shouldDisplayHomeAsUp = b;
}
@NonNull
public abstract Toolbar getToolbar();
public Toolbar getToolbar()
{
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
if (toolbar == null) throw new RuntimeException(
"Your BaseRootView should have a " +
"toolbar with id R.id.toolbar");
return toolbar;
}
public int getToolbarColor()
{
if (SDK_INT < LOLLIPOP && !themeSwitcher.isNightMode())
{
return ResourcesCompat.getColor(context.getResources(),
R.color.grey_900, context.getTheme());
}
StyledResources res = new StyledResources(context);
return res.getColor(R.attr.colorPrimary);
}
@ -86,7 +91,18 @@ public abstract class BaseRootView extends FrameLayout
if (view != null) view.setVisibility(GONE);
view = findViewById(R.id.headerShadow);
if(view != null) view.setVisibility(GONE);
if (view != null) view.setVisibility(GONE);
}
}
public void onAttachedToScreen(BaseScreen screen)
{
this.screen = screen;
}
@Nullable
public BaseScreen getScreen()
{
return screen;
}
}

@ -17,7 +17,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.activities;
package org.isoron.androidbase.activities;
import android.content.*;
import android.graphics.*;
@ -33,14 +33,14 @@ import android.support.v7.widget.Toolbar;
import android.view.*;
import android.widget.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.utils.*;
import org.isoron.androidbase.*;
import org.isoron.androidbase.utils.*;
import java.io.*;
import static android.os.Build.VERSION.*;
import static android.os.Build.VERSION_CODES.*;
import static android.support.v4.content.FileProvider.*;
import static android.os.Build.VERSION.SDK_INT;
import static android.os.Build.VERSION_CODES.LOLLIPOP;
import static android.support.v4.content.FileProvider.getUriForFile;
/**
* Base class for all screens in the application.
@ -51,8 +51,6 @@ import static android.support.v4.content.FileProvider.*;
*/
public class BaseScreen
{
public static final int REQUEST_CREATE_DOCUMENT = 1;
protected BaseActivity activity;
@Nullable
@ -61,13 +59,28 @@ public class BaseScreen
@Nullable
private BaseSelectionMenu selectionMenu;
private Snackbar snackbar;
protected Snackbar snackbar;
public BaseScreen(@NonNull BaseActivity activity)
{
this.activity = activity;
}
@Deprecated
public static int getDefaultActionBarColor(Context context)
{
if (SDK_INT < LOLLIPOP)
{
return ResourcesCompat.getColor(context.getResources(),
R.color.grey_900, context.getTheme());
}
else
{
StyledResources res = new StyledResources(context);
return res.getColor(R.attr.colorPrimary);
}
}
@Deprecated
public static void setupActionBarColor(@NonNull AppCompatActivity activity,
int color)
@ -83,7 +96,6 @@ public class BaseScreen
actionBar.setDisplayHomeAsUpEnabled(true);
ColorDrawable drawable = new ColorDrawable(color);
actionBar.setBackgroundDrawable(drawable);
@ -102,21 +114,6 @@ public class BaseScreen
}
}
@Deprecated
public static int getDefaultActionBarColor(Context context)
{
if (SDK_INT < LOLLIPOP)
{
return ResourcesCompat.getColor(context.getResources(),
R.color.grey_900, context.getTheme());
}
else
{
StyledResources res = new StyledResources(context);
return res.getColor(R.attr.colorPrimary);
}
}
/**
* Notifies the screen that its contents should be updated.
*/
@ -130,7 +127,8 @@ public class BaseScreen
{
if (rootView == null) return;
activity.runOnUiThread(() -> {
activity.runOnUiThread(() ->
{
Toolbar toolbar = rootView.getToolbar();
activity.setSupportActionBar(toolbar);
ActionBar actionBar = activity.getSupportActionBar();
@ -159,6 +157,15 @@ public class BaseScreen
{
}
/**
* Called after activity has been recreated, and the dialogs should be
* reattached to their controllers.
*/
public void reattachDialogs()
{
}
/**
* Sets the menu to be shown by this screen.
* <p>
@ -182,7 +189,7 @@ public class BaseScreen
this.rootView = rootView;
activity.setContentView(rootView);
if (rootView == null) return;
rootView.onAttachedToScreen(this);
invalidateToolbar();
}

@ -17,7 +17,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.activities;
package org.isoron.androidbase.activities;
import android.support.annotation.*;
import android.support.v7.view.ActionMode;
@ -59,16 +59,16 @@ public abstract class BaseSelectionMenu
/**
* Called when the menu is first displayed.
* <p>
* This method cannot be overridden. The application should override the
* methods onCreate(Menu) and getMenuResourceId instead.
* This method should not be overridden. The application should override
* the methods onCreate(Menu) and getMenuResourceId instead.
*
* @param inflater a menu inflater, for creating the menu
* @param mode the action mode associated with this menu.
* @param menu the menu that is being created.
*/
public final void onCreate(@NonNull MenuInflater inflater,
@NonNull ActionMode mode,
@NonNull Menu menu)
public void onCreate(@NonNull MenuInflater inflater,
@NonNull ActionMode mode,
@NonNull Menu menu)
{
this.actionMode = mode;
inflater.inflate(getResourceId(), menu);

@ -17,80 +17,12 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.utils;
package org.isoron.androidbase.utils;
import android.content.*;
import android.graphics.*;
import android.util.*;
public abstract class ColorUtils
{
public static String CSV_PALETTE[] = {
"#D32F2F", // 0 red
"#E64A19", // 1 orange
"#F9A825", // 2 yellow
"#AFB42B", // 3 light green
"#388E3C", // 4 dark green
"#00897B", // 5 teal
"#00ACC1", // 6 cyan
"#039BE5", // 7 blue
"#5E35B1", // 8 deep purple
"#8E24AA", // 9 purple
"#D81B60", // 10 pink
"#303030", // 11 dark grey
"#aaaaaa" // 12 light grey
};
public static int colorToPaletteIndex(Context context, int color)
{
StyledResources res = new StyledResources(context);
int[] palette = res.getPalette();
for (int k = 0; k < palette.length; k++)
if (palette[k] == color) return k;
return -1;
}
public static int getAndroidTestColor(int index)
{
int palette[] = {
Color.parseColor("#D32F2F"), // 0 red
Color.parseColor("#E64A19"), // 1 orange
Color.parseColor("#F9A825"), // 2 yellow
Color.parseColor("#AFB42B"), // 3 light green
Color.parseColor("#388E3C"), // 4 dark green
Color.parseColor("#00897B"), // 5 teal
Color.parseColor("#00ACC1"), // 6 cyan
Color.parseColor("#039BE5"), // 7 blue
Color.parseColor("#5E35B1"), // 8 deep purple
Color.parseColor("#8E24AA"), // 9 purple
Color.parseColor("#D81B60"), // 10 pink
Color.parseColor("#303030"), // 11 dark grey
Color.parseColor("#aaaaaa") // 12 light grey
};
return palette[index];
}
public static int getColor(Context context, int paletteColor)
{
if (context == null)
throw new IllegalArgumentException("Context is null");
StyledResources res = new StyledResources(context);
int palette[] = res.getPalette();
if (paletteColor < 0 || paletteColor >= palette.length)
{
Log.w("ColorHelper",
String.format("Invalid color: %d. Returning default.",
paletteColor));
paletteColor = 0;
}
return palette[paletteColor];
}
public static int mixColors(int color1, int color2, float amount)
{
final byte ALPHA_CHANNEL = 24;

@ -1,5 +1,5 @@
/*
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
* Copyright (C) 2017 Álinson Santos Xavier <isoron@gmail.com>
*
* This file is part of Loop Habit Tracker.
*
@ -17,16 +17,12 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.utils;
package org.isoron.androidbase.utils;
import android.content.*;
import android.os.*;
import android.support.annotation.*;
import android.support.v4.content.*;
import android.util.*;
import org.isoron.uhabits.*;
import java.io.*;
public abstract class FileUtils
@ -54,8 +50,8 @@ public abstract class FileUtils
}
@Nullable
private static File getDir(@NonNull File potentialParentDirs[],
@Nullable String relativePath)
public static File getDir(@NonNull File potentialParentDirs[],
@Nullable String relativePath)
{
if (relativePath == null) relativePath = "";
@ -69,7 +65,7 @@ public abstract class FileUtils
if (chosenDir == null)
{
Log.e("DatabaseHelper",
Log.e("FileUtils",
"getDir: all potential parents are null or non-writable");
return null;
}
@ -78,7 +74,7 @@ public abstract class FileUtils
String.format("%s/%s/", chosenDir.getAbsolutePath(), relativePath));
if (!dir.exists() && !dir.mkdirs())
{
Log.e("DatabaseHelper",
Log.e("FileUtils",
"getDir: chosen dir does not exist and cannot be created");
return null;
}
@ -86,22 +82,6 @@ public abstract class FileUtils
return dir;
}
@Nullable
public static File getFilesDir(@NonNull Context context, @Nullable String relativePath)
{
File externalFilesDirs[] =
ContextCompat.getExternalFilesDirs(context, null);
if (externalFilesDirs == null)
{
Log.e("DatabaseHelper",
"getFilesDir: getExternalFilesDirs returned null");
return null;
}
return getDir(externalFilesDirs, relativePath);
}
@Nullable
public static File getSDCardDir(@Nullable String relativePath)
{

@ -17,29 +17,42 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.utils;
package org.isoron.androidbase.utils;
import android.content.*;
import android.content.res.*;
import android.graphics.*;
import android.support.annotation.*;
import android.support.v4.view.*;
import android.util.*;
import android.view.*;
import android.widget.*;
public abstract class InterfaceUtils
{
private static Typeface fontAwesome;
@Nullable
private static Float fixedResolution = null;
public static void setFixedResolution(@NonNull Float f)
{
fixedResolution = f;
}
public static Typeface getFontAwesome(Context context)
{
if(fontAwesome == null)
fontAwesome = Typeface.createFromAsset(context.getAssets(), "fontawesome-webfont.ttf");
if(fontAwesome == null) fontAwesome =
Typeface.createFromAsset(context.getAssets(),
"fontawesome-webfont.ttf");
return fontAwesome;
}
public static float dpToPixels(Context context, float dp)
{
if(fixedResolution != null) return dp * fixedResolution;
Resources resources = context.getResources();
DisplayMetrics metrics = resources.getDisplayMetrics();
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, metrics);
@ -47,11 +60,40 @@ public abstract class InterfaceUtils
public static float spToPixels(Context context, float sp)
{
if(fixedResolution != null) return sp * fixedResolution;
Resources resources = context.getResources();
DisplayMetrics metrics = resources.getDisplayMetrics();
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, metrics);
}
public static float getDimension(Context context, int id)
{
float dim = context.getResources().getDimension(id);
if (fixedResolution == null) return dim;
else
{
DisplayMetrics dm = context.getResources().getDisplayMetrics();
float actualDensity = dm.density;
return dim / actualDensity * fixedResolution;
}
}
public static void setupEditorAction(@NonNull ViewGroup parent,
@NonNull TextView.OnEditorActionListener listener)
{
for (int i = 0; i < parent.getChildCount(); i++)
{
View child = parent.getChildAt(i);
if (child instanceof ViewGroup)
setupEditorAction((ViewGroup) child, listener);
if (child instanceof TextView)
((TextView) child).setOnEditorActionListener(listener);
}
}
public static boolean isLayoutRtl(View view)
{
return ViewCompat.getLayoutDirection(view) ==

@ -17,14 +17,14 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.isoron.uhabits.utils;
package org.isoron.androidbase.utils;
import android.content.*;
import android.content.res.*;
import android.graphics.drawable.*;
import android.support.annotation.*;
import org.isoron.uhabits.*;
import org.isoron.androidbase.*;
public class StyledResources
{
@ -51,6 +51,15 @@ public class StyledResources
return bool;
}
public int getDimension(@AttrRes int attrId)
{
TypedArray ta = getTypedArray(attrId);
int dim = ta.getDimensionPixelSize(0, 0);
ta.recycle();
return dim;
}
public int getColor(@AttrRes int attrId)
{
TypedArray ta = getTypedArray(attrId);
@ -80,13 +89,13 @@ public class StyledResources
public int[] getPalette()
{
int resourceId = getStyleResource(R.attr.palette);
int resourceId = getResource(R.attr.palette);
if (resourceId < 0) throw new RuntimeException("resource not found");
return context.getResources().getIntArray(resourceId);
}
int getStyleResource(@AttrRes int attrId)
public int getResource(@AttrRes int attrId)
{
TypedArray ta = getTypedArray(attrId);
int resourceId = ta.getResourceId(0, -1);

@ -0,0 +1,6 @@
<resources>
<item name="toolbar" type="id" />
<item name="toolbarShadow" type="id" />
<item name="headerShadow" type="id" />
<attr name="palette" format="reference"/>
</resources>

@ -1,97 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2013 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<array name="lightPalette">
<item>@color/red_700</item>
<item>@color/deep_orange_700</item>
<item>@color/yellow_800</item>
<item>@color/lime_700</item>
<item>@color/green_700</item>
<item>@color/teal_600</item>
<item>@color/cyan_600</item>
<item>@color/light_blue_600</item>
<item>@color/deep_purple_600</item>
<item>@color/purple_600</item>
<item>@color/pink_600</item>
<item>@color/grey_800</item>
<item>@color/grey_500</item>
</array>
<array name="darkPalette">
<item>@color/red_200</item>
<item>@color/deep_orange_200</item>
<item>@color/yellow_200</item>
<item>@color/lime_200</item>
<item>@color/green_A200</item>
<item>@color/teal_200</item>
<item>@color/cyan_200</item>
<item>@color/light_blue_200</item>
<item>@color/deep_purple_200</item>
<item>@color/purple_200</item>
<item>@color/pink_200</item>
<item>@color/grey_100</item>
<item>@color/grey_500</item>
</array>
<array name="transparentWidgetPalette">
<item>@color/red_800</item>
<item>@color/deep_orange_800</item>
<item>@color/yellow_800</item>
<item>@color/lime_800</item>
<item>@color/green_700</item>
<item>@color/teal_700</item>
<item>@color/cyan_700</item>
<item>@color/light_blue_700</item>
<item>@color/deep_purple_700</item>
<item>@color/purple_700</item>
<item>@color/pink_700</item>
<item>@color/black_aa</item>
<item>@color/black_aa</item>
</array>
<!-- Time and Date picker -->
<color name="circle_background">#f2f2f2</color>
<color name="line_background">#cccccc</color>
<color name="ampm_text_color">#8c8c8c</color>
<color name="done_text_color_normal">#000000</color>
<color name="done_text_color_disabled">#cccccc</color>
<color name="numbers_text_color">#8c8c8c</color>
<color name="transparent">#00000000</color>
<color name="transparent_black">#7f000000</color>
<color name="blue">#33b5e5</color>
<color name="blue_focused">#c1e8f7</color>
<color name="neutral_pressed">#33999999</color>
<color name="darker_blue">#0099cc</color>
<color name="date_picker_text_normal">#ff999999</color>
<color name="calendar_header">#999999</color>
<color name="date_picker_view_animator">#f2f2f2</color>
<color name="calendar_selected_date_text">#ffd1d2d4</color>
<color name="done_text_color">#888888</color>
<color name="done_text_color_dark">#888888</color>
<!-- Colors for red theme -->
<color name="red">#ff3333</color>
<color name="red_focused">#853333</color>
<color name="light_gray">#404040</color>
<color name="dark_gray">#363636</color>
<color name="line_dark">#808080</color>
<!-- Material design color palette -->
<color name="red_50">#FFEBEE</color>
<color name="red_100">#FFCDD2</color>
<color name="red_200">#EF9A9A</color>

@ -0,0 +1 @@
/build

@ -0,0 +1,25 @@
apply plugin: 'com.android.library'
android {
compileSdkVersion COMPILE_SDK_VERSION as Integer
buildToolsVersion BUILD_TOOLS_VERSION
defaultConfig {
minSdkVersion MIN_SDK_VERSION as Integer
targetSdkVersion TARGET_SDK_VERSION as Integer
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation "com.android.support:appcompat-v7:$SUPPORT_LIBRARY_VERSION"
}

@ -0,0 +1,25 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /gemini-b/opt/android-sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

@ -0,0 +1,2 @@
<manifest package="com.android"
xmlns:android="http://schemas.android.com/apk/res/android"/>

@ -16,18 +16,15 @@
package com.android.colorpicker;
import android.app.Activity;
import android.app.Dialog;
import android.os.Bundle;
import android.app.*;
import android.os.*;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatDialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ProgressBar;
import android.support.v7.app.*;
import android.view.*;
import android.widget.*;
import com.android.colorpicker.ColorPickerSwatch.OnColorSelectedListener;
import org.isoron.uhabits.R;
import com.android.*;
import com.android.colorpicker.ColorPickerSwatch.*;
/**
* A dialog which takes in as input an array of palette and creates a palette allowing the user to

@ -16,18 +16,14 @@
package com.android.colorpicker;
import org.isoron.uhabits.R;
import android.content.Context;
import android.content.res.Resources;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TableLayout;
import android.widget.TableRow;
import com.android.colorpicker.ColorPickerSwatch.OnColorSelectedListener;
import android.content.*;
import android.content.res.*;
import android.util.*;
import android.view.*;
import android.widget.*;
import com.android.*;
import com.android.colorpicker.ColorPickerSwatch.*;
/**
* A color picker custom view which creates an grid of color squares. The number of squares per

@ -16,14 +16,12 @@
package com.android.colorpicker;
import org.isoron.uhabits.R;
import android.content.*;
import android.graphics.drawable.*;
import android.view.*;
import android.widget.*;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import com.android.*;
/**
* Creates a circular swatch of a specified color. Adds a checkmark if marked as checked.

@ -16,36 +16,23 @@
package com.android.datetimepicker.date;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Locale;
import org.isoron.uhabits.R;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.app.DialogFragment;
import android.content.res.Resources;
import android.os.Bundle;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.datetimepicker.HapticFeedbackController;
import com.android.datetimepicker.Utils;
import com.android.datetimepicker.date.MonthAdapter.CalendarDay;
import android.animation.*;
import android.app.*;
import android.content.res.*;
import android.os.*;
import android.text.format.*;
import android.util.*;
import android.view.*;
import android.view.View.*;
import android.view.animation.*;
import android.widget.*;
import com.android.*;
import com.android.datetimepicker.*;
import com.android.datetimepicker.date.MonthAdapter.*;
import java.text.*;
import java.util.*;
/**
* Dialog allowing users to select a date.

@ -16,37 +16,25 @@
package com.android.datetimepicker.date;
import java.security.InvalidParameterException;
import java.util.Calendar;
import android.content.*;
import android.content.res.*;
import android.graphics.*;
import android.graphics.Paint.*;
import android.os.*;
import android.support.v4.view.*;
import android.support.v4.view.accessibility.*;
import android.support.v4.widget.*;
import android.text.format.*;
import android.view.*;
import android.view.accessibility.*;
import com.android.*;
import com.android.datetimepicker.*;
import com.android.datetimepicker.date.MonthAdapter.*;
import java.security.*;
import java.util.*;
import java.util.Formatter;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import org.isoron.uhabits.R;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Paint.Style;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.os.Bundle;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
import android.support.v4.widget.ExploreByTouchHelper;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.text.format.Time;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import com.android.datetimepicker.Utils;
import com.android.datetimepicker.date.MonthAdapter.CalendarDay;
/**
* A calendar-like view displaying a specified month and the appropriate selectable day numbers

@ -16,16 +16,14 @@
package com.android.datetimepicker.date;
import org.isoron.uhabits.R;
import android.content.*;
import android.content.res.*;
import android.graphics.*;
import android.graphics.Paint.*;
import android.util.*;
import android.widget.*;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Paint.Style;
import android.util.AttributeSet;
import android.widget.TextView;
import com.android.*;
/**
* A text view which, when pressed or activated, displays a blue circle around the text.

@ -16,24 +16,18 @@
package com.android.datetimepicker.date;
import java.util.ArrayList;
import java.util.List;
import org.isoron.uhabits.R;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.StateListDrawable;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import com.android.datetimepicker.date.DatePickerDialog.OnDateChangedListener;
import android.content.*;
import android.content.res.*;
import android.graphics.drawable.*;
import android.view.*;
import android.view.accessibility.*;
import android.widget.*;
import android.widget.AdapterView.*;
import com.android.*;
import com.android.datetimepicker.date.DatePickerDialog.*;
import java.util.*;
/**
* Displays a selectable list of years.

@ -16,20 +16,17 @@
package com.android.datetimepicker.time;
import java.text.DateFormatSymbols;
import android.content.*;
import android.content.res.*;
import android.graphics.*;
import android.graphics.Paint.*;
import android.util.*;
import android.view.*;
import org.isoron.uhabits.R;
import com.android.*;
import com.android.datetimepicker.*;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Typeface;
import android.util.Log;
import android.view.View;
import com.android.datetimepicker.Utils;
import java.text.*;
/**
* Draw the two smaller AM and PM circles next to where the larger circle will be.

@ -16,14 +16,14 @@
package com.android.datetimepicker.time;
import org.isoron.uhabits.R;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.Log;
import android.view.View;
import android.content.*;
import android.content.res.*;
import android.graphics.*;
import android.util.*;
import android.view.*;
import com.android.*;
/**
* Draws a simple white circle on which the numbers will be drawn.

@ -16,30 +16,20 @@
package com.android.datetimepicker.time;
import org.isoron.uhabits.R;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Handler;
import android.text.format.DateUtils;
import android.text.format.Time;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import com.android.datetimepicker.HapticFeedbackController;
import android.animation.*;
import android.annotation.*;
import android.content.*;
import android.content.res.*;
import android.os.*;
import android.text.format.*;
import android.util.*;
import android.view.*;
import android.view.View.*;
import android.view.accessibility.*;
import android.widget.*;
import com.android.*;
import com.android.datetimepicker.*;
/**
* The primary layout to hold the circular picker, and the am/pm buttons. This view well measure

@ -16,21 +16,16 @@
package com.android.datetimepicker.time;
import org.isoron.uhabits.R;
import android.animation.Keyframe;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.Log;
import android.view.View;
import com.android.datetimepicker.Utils;
import android.animation.*;
import android.animation.ValueAnimator.*;
import android.content.*;
import android.content.res.*;
import android.graphics.*;
import android.util.*;
import android.view.*;
import com.android.*;
import com.android.datetimepicker.*;
/**
* View to show what number is selected. This will draw a blue circle over the number, with a blue

@ -16,21 +16,16 @@
package com.android.datetimepicker.time;
import org.isoron.uhabits.R;
import android.animation.Keyframe;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Typeface;
import android.util.Log;
import android.view.View;
import android.animation.*;
import android.animation.ValueAnimator.*;
import android.content.*;
import android.content.res.*;
import android.graphics.*;
import android.graphics.Paint.*;
import android.util.*;
import android.view.*;
import com.android.*;
/**
* A view to show a series of numbers in a circular pattern.

@ -16,37 +16,25 @@
package com.android.datetimepicker.time;
import java.text.DateFormatSymbols;
import java.util.ArrayList;
import java.util.Locale;
import org.isoron.uhabits.R;
import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.animation.*;
import android.annotation.*;
import android.app.ActionBar.*;
import android.app.*;
import android.app.ActionBar;
import android.app.ActionBar.LayoutParams;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.os.Bundle;
import android.content.*;
import android.content.res.*;
import android.os.*;
import android.support.v7.app.*;
import android.util.Log;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnKeyListener;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.android.datetimepicker.HapticFeedbackController;
import com.android.datetimepicker.Utils;
import com.android.datetimepicker.time.RadialPickerLayout.OnValueSelectedListener;
import android.util.*;
import android.view.*;
import android.view.View.*;
import android.widget.*;
import com.android.*;
import com.android.datetimepicker.*;
import com.android.datetimepicker.time.RadialPickerLayout.*;
import java.text.*;
import java.util.*;
/**
* Dialog to set a time.

@ -24,16 +24,36 @@
<dimen name="color_swatch_margins_large">8dip</dimen>
<dimen name="color_swatch_margins_small">4dip</dimen>
<item name="circle_radius_multiplier" format="float" type="string" translatable="false">0.82</item>
<item name="circle_radius_multiplier_24HourMode" format="float" type="string" translatable="false">0.85</item>
<item name="selection_radius_multiplier" format="float" type="string" translatable="false">0.16</item>
<item name="ampm_circle_radius_multiplier" format="float" type="string" translatable="false">0.19</item>
<item name="numbers_radius_multiplier_normal" format="float" type="string" translatable="false">0.81</item>
<item name="numbers_radius_multiplier_inner" format="float" type="string" translatable="false">0.60</item>
<item name="numbers_radius_multiplier_outer" format="float" type="string" translatable="false">0.83</item>
<item name="text_size_multiplier_normal" format="float" type="string" translatable="false">0.17</item>
<item name="text_size_multiplier_inner" format="float" type="string" translatable="false">0.14</item>
<item name="text_size_multiplier_outer" format="float" type="string" translatable="false">0.11</item>
<item name="circle_radius_multiplier" format="float" translatable="false" type="string">
0.82
</item>
<item name="circle_radius_multiplier_24HourMode" format="float" translatable="false" type="string">
0.85
</item>
<item name="selection_radius_multiplier" format="float" translatable="false" type="string">
0.16
</item>
<item name="ampm_circle_radius_multiplier" format="float" translatable="false" type="string">
0.19
</item>
<item name="numbers_radius_multiplier_normal" format="float" translatable="false" type="string">
0.81
</item>
<item name="numbers_radius_multiplier_inner" format="float" translatable="false" type="string">
0.60
</item>
<item name="numbers_radius_multiplier_outer" format="float" translatable="false" type="string">
0.83
</item>
<item name="text_size_multiplier_normal" format="float" translatable="false" type="string">
0.17
</item>
<item name="text_size_multiplier_inner" format="float" translatable="false" type="string">
0.14
</item>
<item name="text_size_multiplier_outer" format="float" translatable="false" type="string">
0.11
</item>
<dimen name="time_label_size">60sp</dimen>
<dimen name="extra_time_label_margin">-30dp</dimen>
@ -64,8 +84,8 @@
<dimen name="year_label_height">64dp</dimen>
<dimen name="year_label_text_size">22sp</dimen>
<string name="color_swatch_description" translatable="false">Color <xliff:g id="color_index" example="14">%1$d</xliff:g></string>
<string name="color_swatch_description_selected" translatable="false">Color <xliff:g id="color_index" example="14">%1$d</xliff:g> selected</string>
<string name="color_swatch_description" translatable="false">Color <xliff:g example="14" id="color_index">%1$d</xliff:g></string>
<string name="color_swatch_description_selected" translatable="false">Color <xliff:g example="14" id="color_index">%1$d</xliff:g> selected</string>
<!-- Date and time picker -->
<string name="hour_picker_description" translatable="false">Hours circular slider</string>
@ -74,11 +94,56 @@
<string name="year_picker_description" translatable="false">Year list</string>
<string name="select_day" translatable="false">Select month and day</string>
<string name="select_year" translatable="false">Select year</string>
<string name="item_is_selected" translatable="false"><xliff:g id="item" example="2013">%1$s</xliff:g> selected</string>
<string name="deleted_key" translatable="false"><xliff:g id="key" example="4">%1$s</xliff:g> deleted</string>
<string name="item_is_selected" translatable="false"><xliff:g example="2013" id="item">%1$s</xliff:g> selected</string>
<string name="deleted_key" translatable="false"><xliff:g example="4" id="key">%1$s</xliff:g> deleted</string>
<string name="time_placeholder" translatable="false">--</string>
<string name="time_separator" translatable="false">:</string>
<string name="radial_numbers_typeface" translatable="false">sans-serif</string>
<string name="sans_serif" translatable="false">sans-serif</string>
<string name="day_of_week_label_typeface" translatable="false">sans-serif</string>
<!-- Time and Date picker -->
<color name="circle_background">#f2f2f2</color>
<color name="line_background">#cccccc</color>
<color name="ampm_text_color">#8c8c8c</color>
<color name="done_text_color_normal">#000000</color>
<color name="done_text_color_disabled">#cccccc</color>
<color name="numbers_text_color">#8c8c8c</color>
<color name="transparent">#00000000</color>
<color name="transparent_black">#7f000000</color>
<color name="blue">#33b5e5</color>
<color name="blue_focused">#c1e8f7</color>
<color name="neutral_pressed">#33999999</color>
<color name="darker_blue">#0099cc</color>
<color name="date_picker_text_normal">#ff999999</color>
<color name="calendar_header">#999999</color>
<color name="date_picker_view_animator">#f2f2f2</color>
<color name="calendar_selected_date_text">#ffd1d2d4</color>
<color name="done_text_color">#888888</color>
<color name="done_text_color_dark">#888888</color>
<color name="white">#ffffff</color>
<color name="black">#000000</color>
<!-- Colors for red theme -->
<color name="red">#ff3333</color>
<color name="red_focused">#853333</color>
<color name="light_gray">#404040</color>
<color name="dark_gray">#363636</color>
<color name="line_dark">#808080</color>
<style name="time_label">
<item name="android:textSize">@dimen/time_label_size</item>
<item name="android:textColor">@color/numbers_text_color</item>
</style>
<style name="ampm_label">
<item name="android:textSize">@dimen/ampm_label_size</item>
<item name="android:textAllCaps">true</item>
<item name="android:textColor">@color/ampm_text_color</item>
<item name="android:textStyle">bold</item>
</style>
<style name="TimePickerDialog" parent="@style/Theme.AppCompat.Light.Dialog">
<item name="windowNoTitle">true</item>
</style>
</resources>

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="select_hours"/>
<string name="select_minutes"/>
<string name="color_picker_default_title"/>
<string name="clear"/>
<string name="clear_label"/>
<string name="done_label"/>
</resources>

@ -1,3 +0,0 @@
-dontwarn java.beans.**
-dontwarn java.lang.**
-dontobfuscate

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

@ -1,5 +0,0 @@
#!/bin/bash
P=/sdcard/Android/data/org.isoron.uhabits/cache/Failed/
adb pull $P Failed/
adb shell rm -r $P

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 551 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 505 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 559 B

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save