synced with original repository

pull/236/head
AnirudhaAgashe 9 years ago
commit e4f41560fc

@ -4,13 +4,13 @@ apply plugin: 'me.tatarka.retrolambda'
apply plugin: 'jacoco'
android {
compileSdkVersion 23
buildToolsVersion "23.0.3"
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
applicationId "org.isoron.uhabits"
minSdkVersion 15
targetSdkVersion 23
targetSdkVersion 25
buildConfigField "Integer", "databaseVersion", "15"
buildConfigField "String", "databaseFilename", "\"uhabits.db\""
@ -53,7 +53,7 @@ dependencies {
androidTestApt 'com.google.dagger:dagger-compiler:2.2'
androidTestCompile 'com.android.support:support-annotations:23.3.0'
androidTestCompile 'com.android.support:support-annotations:25.1.0'
androidTestCompile 'com.android.support.test:rules:0.5'
androidTestCompile 'com.android.support.test:runner:0.5'
androidTestCompile 'com.google.auto.factory:auto-factory:1.0-beta3'
@ -64,10 +64,10 @@ dependencies {
apt 'com.google.dagger:dagger-compiler:2.2'
apt 'com.jakewharton:butterknife-compiler:8.0.1'
compile 'com.android.support:appcompat-v7:23.3.0'
compile 'com.android.support:design:23.3.0'
compile 'com.android.support:preference-v14:23.3.0'
compile 'com.android.support:support-v4:23.3.0'
compile 'com.android.support:appcompat-v7:25.1.0'
compile 'com.android.support:design:25.1.0'
compile 'com.android.support:preference-v14:25.1.0'
compile 'com.android.support:support-v4:25.1.0'
compile 'com.getpebble:pebblekit:3.0.0'
compile 'com.github.paolorotolo:appintro:3.4.0'
compile 'com.google.auto.factory:auto-factory:1.0-beta3'

@ -223,5 +223,15 @@
</intent-filter>
</receiver>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="org.isoron.uhabits"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>
</manifest>

@ -40,6 +40,7 @@ import java.io.*;
import static android.os.Build.VERSION.*;
import static android.os.Build.VERSION_CODES.*;
import static android.support.v4.content.FileProvider.*;
/**
* Base class for all screens in the application.
@ -50,6 +51,8 @@ import static android.os.Build.VERSION_CODES.*;
*/
public class BaseScreen
{
public static final int REQUEST_CREATE_DOCUMENT = 1;
protected BaseActivity activity;
@Nullable
@ -230,11 +233,14 @@ public class BaseScreen
public void showSendFileScreen(@NonNull String archiveFilename)
{
File file = new File(archiveFilename);
Uri fileUri = getUriForFile(activity, "org.isoron.uhabits", file);
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.setType("application/zip");
intent.putExtra(Intent.EXTRA_STREAM,
Uri.fromFile(new File(archiveFilename)));
intent.putExtra(Intent.EXTRA_STREAM, fileUri);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
activity.startActivity(intent);
}

@ -25,6 +25,7 @@ import org.isoron.uhabits.*;
import org.isoron.uhabits.activities.*;
import org.isoron.uhabits.activities.habits.list.model.*;
import org.isoron.uhabits.preferences.*;
import org.isoron.uhabits.utils.*;
/**
* Activity that allows the user to see and modify the list of habits.
@ -43,6 +44,8 @@ public class ListHabitsActivity extends BaseActivity
private Preferences prefs;
private MidnightTimer midnightTimer;
public ListHabitsComponent getListHabitsComponent()
{
return component;
@ -77,6 +80,8 @@ public class ListHabitsActivity extends BaseActivity
screen.setSelectionMenu(selectionMenu);
rootView.setController(controller, selectionMenu);
midnightTimer = component.getMidnightTimer();
setScreen(screen);
controller.onStartup();
}
@ -84,6 +89,7 @@ public class ListHabitsActivity extends BaseActivity
@Override
protected void onPause()
{
midnightTimer.onPause();
screen.onDettached();
adapter.cancelRefresh();
super.onPause();
@ -95,6 +101,7 @@ public class ListHabitsActivity extends BaseActivity
adapter.refresh();
screen.onAttached();
rootView.postInvalidate();
midnightTimer.onResume();
if (prefs.getTheme() == ThemeSwitcher.THEME_DARK &&
prefs.isPureBlackEnabled() != pureBlack)

@ -23,13 +23,14 @@ import org.isoron.uhabits.*;
import org.isoron.uhabits.activities.*;
import org.isoron.uhabits.activities.habits.list.controllers.*;
import org.isoron.uhabits.activities.habits.list.model.*;
import org.isoron.uhabits.utils.*;
import dagger.*;
@ActivityScope
@Component(modules = { ActivityModule.class },
dependencies = { AppComponent.class })
public interface ListHabitsComponent extends ActivityComponent
public interface ListHabitsComponent
{
CheckmarkButtonControllerFactory getCheckmarkButtonControllerFactory();
@ -44,4 +45,6 @@ public interface ListHabitsComponent extends ActivityComponent
ListHabitsScreen getScreen();
ListHabitsSelectionMenu getSelectionMenu();
MidnightTimer getMidnightTimer();
}

@ -133,7 +133,8 @@ public class ListHabitsController
taskRunner.execute(() -> habitList.reorder(from, to));
}
public void onImportData(@NonNull File file)
public void onImportData(@NonNull File file,
@NonNull OnFinishedListener finishedListener)
{
taskRunner.execute(importTaskFactory.create(file, result -> {
switch (result)
@ -151,6 +152,8 @@ public class ListHabitsController
screen.showMessage(R.string.could_not_import);
break;
}
finishedListener.onFinish();
}));
}
@ -213,4 +216,9 @@ public class ListHabitsController
prefs.updateLastHint(-1, DateUtils.getStartOfToday());
screen.showIntroScreen();
}
public interface OnFinishedListener
{
void onFinish();
}
}

@ -19,7 +19,9 @@
package org.isoron.uhabits.activities.habits.list;
import android.app.*;
import android.content.*;
import android.net.*;
import android.support.annotation.*;
import org.isoron.uhabits.*;
@ -31,24 +33,32 @@ import org.isoron.uhabits.commands.*;
import org.isoron.uhabits.intents.*;
import org.isoron.uhabits.io.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.utils.*;
import java.io.*;
import javax.inject.*;
import static android.os.Build.VERSION.*;
import static android.os.Build.VERSION_CODES.*;
@ActivityScope
public class ListHabitsScreen extends BaseScreen
implements CommandRunner.Listener
{
public static final int RESULT_BUG_REPORT = 4;
public static final int RESULT_IMPORT_DATA = 1;
public static final int RESULT_EXPORT_CSV = 2;
public static final int RESULT_EXPORT_DB = 3;
public static final int RESULT_BUG_REPORT = 4;
public static final int RESULT_REPAIR_DB = 5;
public static final int RESULT_IMPORT_DATA = 1;
public static final int REQUEST_OPEN_DOCUMENT = 6;
public static final int REQUEST_SETTINGS = 7;
@Nullable
private ListHabitsController controller;
@ -125,6 +135,15 @@ public class ListHabitsScreen extends BaseScreen
@Override
public void onResult(int requestCode, int resultCode, Intent data)
{
if (requestCode == REQUEST_OPEN_DOCUMENT)
onOpenDocumentResult(resultCode, data);
if (requestCode == REQUEST_SETTINGS)
onSettingsResult(resultCode);
}
private void onSettingsResult(int resultCode)
{
if (controller == null) return;
@ -152,6 +171,30 @@ public class ListHabitsScreen extends BaseScreen
}
}
private void onOpenDocumentResult(int resultCode, Intent data)
{
if (controller == null) return;
if (resultCode != Activity.RESULT_OK) return;
try
{
Uri uri = data.getData();
ContentResolver cr = activity.getContentResolver();
InputStream is = cr.openInputStream(uri);
File cacheDir = activity.getExternalCacheDir();
File tempFile = File.createTempFile("import", "", cacheDir);
FileUtils.copy(is, tempFile);
controller.onImportData(tempFile, () -> tempFile.delete());
}
catch (IOException e)
{
showMessage(R.string.could_not_import);
e.printStackTrace();
}
}
public void setController(@Nullable ListHabitsController controller)
{
this.controller = controller;
@ -208,6 +251,21 @@ public class ListHabitsScreen extends BaseScreen
}
public void showImportScreen()
{
if (SDK_INT < KITKAT)
{
showImportScreenPreKitKat();
return;
}
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
activity.startActivityForResult(intent, REQUEST_OPEN_DOCUMENT);
}
public void showImportScreenPreKitKat()
{
File dir = dirFinder.findStorageDir(null);
@ -220,7 +278,8 @@ public class ListHabitsScreen extends BaseScreen
FilePickerDialog picker = filePickerDialogFactory.create(dir);
if (controller != null)
picker.setListener(file -> controller.onImportData(file));
picker.setListener(file -> controller.onImportData(file, () -> {}));
activity.showDialog(picker.getDialog());
}
@ -233,7 +292,7 @@ public class ListHabitsScreen extends BaseScreen
public void showSettingsScreen()
{
Intent intent = intentFactory.startSettingsActivity(activity);
activity.startActivityForResult(intent, 0);
activity.startActivityForResult(intent, REQUEST_SETTINGS);
}
public void toggleNightMode()

@ -28,6 +28,7 @@ import org.isoron.uhabits.activities.habits.list.*;
import org.isoron.uhabits.activities.habits.list.views.*;
import org.isoron.uhabits.models.*;
import org.isoron.uhabits.preferences.*;
import org.isoron.uhabits.utils.*;
import java.util.*;
@ -42,7 +43,7 @@ import javax.inject.*;
@ActivityScope
public class HabitCardListAdapter
extends RecyclerView.Adapter<HabitCardViewHolder>
implements HabitCardListCache.Listener
implements HabitCardListCache.Listener, MidnightTimer.MidnightListener
{
@NonNull
private ModelObservable observable;
@ -59,15 +60,20 @@ public class HabitCardListAdapter
@NonNull
private Preferences preferences;
private final MidnightTimer midnightTimer;
@Inject
public HabitCardListAdapter(@NonNull HabitCardListCache cache,
@NonNull Preferences preferences)
@NonNull Preferences preferences,
@NonNull MidnightTimer midnightTimer)
{
this.preferences = preferences;
this.selected = new LinkedList<>();
this.observable = new ModelObservable();
this.cache = cache;
this.midnightTimer = midnightTimer;
cache.setListener(this);
cache.setCheckmarkCount(ListHabitsRootView.MAX_CHECKMARK_COUNT);
cache.setOrder(preferences.getDefaultOrder());
@ -75,6 +81,12 @@ public class HabitCardListAdapter
setHasStableIds(true);
}
@Override
public void atMidnight()
{
cache.refreshAllHabits();
}
public void cancelRefresh()
{
cache.cancelTasks();
@ -148,6 +160,7 @@ public class HabitCardListAdapter
public void onAttached()
{
cache.onAttached();
midnightTimer.addListener(this);
}
@Override
@ -180,6 +193,7 @@ public class HabitCardListAdapter
public void onDetached()
{
cache.onDetached();
midnightTimer.removeListener(this);
}
@Override

@ -26,12 +26,14 @@ import android.view.*;
import android.widget.*;
import org.isoron.uhabits.*;
import org.isoron.uhabits.activities.habits.list.*;
import org.isoron.uhabits.preferences.*;
import org.isoron.uhabits.utils.*;
import java.util.*;
public class HeaderView extends LinearLayout implements Preferences.Listener
public class HeaderView extends LinearLayout
implements Preferences.Listener, MidnightTimer.MidnightListener
{
private final Context context;
@ -40,6 +42,9 @@ public class HeaderView extends LinearLayout implements Preferences.Listener
@Nullable
private Preferences prefs;
@Nullable
private MidnightTimer midnightTimer;
public HeaderView(Context context, AttributeSet attrs)
{
super(context, attrs);
@ -56,6 +61,18 @@ public class HeaderView extends LinearLayout implements Preferences.Listener
HabitsApplication app = (HabitsApplication) appContext;
prefs = app.getComponent().getPreferences();
}
if (context instanceof ListHabitsActivity)
{
ListHabitsActivity activity = (ListHabitsActivity) context;
midnightTimer = activity.getListHabitsComponent().getMidnightTimer();
}
}
@Override
public void atMidnight()
{
post(() -> createButtons());
}
@Override
@ -75,11 +92,13 @@ public class HeaderView extends LinearLayout implements Preferences.Listener
{
super.onAttachedToWindow();
if (prefs != null) prefs.addListener(this);
if (midnightTimer != null) midnightTimer.addListener(this);
}
@Override
protected void onDetachedFromWindow()
{
if (midnightTimer != null) midnightTimer.removeListener(this);
if (prefs != null) prefs.removeListener(this);
super.onDetachedFromWindow();
}

@ -187,6 +187,11 @@ public abstract class DateUtils
return getStartOfDay(DateUtils.getLocalTime());
}
public static long millisecondsUntilTomorrow()
{
return getStartOfToday() + millisecondsInOneDay - getLocalTime();
}
public static GregorianCalendar getStartOfTodayCalendar()
{
return getCalendar(getStartOfToday());

@ -0,0 +1,77 @@
/*
* 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.utils;
import org.isoron.uhabits.activities.*;
import java.util.*;
import java.util.concurrent.*;
import javax.inject.*;
/**
* A class that emits events when a new day starts.
*/
@ActivityScope
public class MidnightTimer
{
private final List<MidnightListener> listeners;
private ScheduledExecutorService executor;
@Inject
public MidnightTimer()
{
this.listeners = new LinkedList<>();
}
public synchronized void addListener(MidnightListener listener)
{
this.listeners.add(listener);
}
public synchronized void onPause()
{
executor.shutdownNow();
}
public synchronized void onResume()
{
executor = Executors.newSingleThreadScheduledExecutor();
executor.scheduleAtFixedRate(() -> notifyListeners(),
DateUtils.millisecondsUntilTomorrow() + 1000,
DateUtils.millisecondsInOneDay, TimeUnit.MILLISECONDS);
}
public synchronized void removeListener(MidnightListener listener)
{
this.listeners.remove(listener);
}
private synchronized void notifyListeners()
{
for (MidnightListener l : listeners) l.atMidnight();
}
public interface MidnightListener
{
void atMidnight();
}
}

@ -41,12 +41,12 @@
<dimen name="done_label_size">14sp</dimen>
<dimen name="ampm_left_padding">6dip</dimen>
<dimen name="separator_padding">4dip</dimen>
<dimen name="header_height">96dip</dimen>
<dimen name="header_height">80dip</dimen>
<dimen name="footer_height">48dip</dimen>
<dimen name="minimum_margin_sides">48dip</dimen>
<dimen name="minimum_margin_top_bottom">24dip</dimen>
<dimen name="picker_dimen">270dip</dimen>
<dimen name="date_picker_component_width">270dp</dimen>
<dimen name="picker_dimen">250dip</dimen>
<dimen name="date_picker_component_width">250dp</dimen>
<dimen name="date_picker_header_height">30dp</dimen>
<dimen name="selected_calendar_layout_height">155dp</dimen>
<dimen name="date_picker_view_animator_height">270dp</dimen>

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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/>.
-->
<paths>
<files-path name="files" path="." />
<cache-path name="cache" path="." />
<external-path name="external" path="." />
<extendal-cache-path name="external-cache" path="." />
<external-files-path name="external-files" path="." />
</paths>

@ -38,10 +38,11 @@ import org.junit.runners.*;
import java.io.*;
import static org.isoron.uhabits.activities.habits.list.ListHabitsScreen.*;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.eq;
@RunWith(JUnit4.class)
public class ListHabitsScreenTest extends BaseUnitTest
@ -124,28 +125,28 @@ public class ListHabitsScreenTest extends BaseUnitTest
@Test
public void testOnResult_bugReport()
{
screen.onResult(0, ListHabitsScreen.RESULT_BUG_REPORT, null);
screen.onResult(REQUEST_SETTINGS, RESULT_BUG_REPORT, null);
verify(controller).onSendBugReport();
}
@Test
public void testOnResult_exportCSV()
{
screen.onResult(0, ListHabitsScreen.RESULT_EXPORT_CSV, null);
screen.onResult(REQUEST_SETTINGS, RESULT_EXPORT_CSV, null);
verify(controller).onExportCSV();
}
@Test
public void testOnResult_exportDB()
{
screen.onResult(0, ListHabitsScreen.RESULT_EXPORT_DB, null);
screen.onResult(REQUEST_SETTINGS, RESULT_EXPORT_DB, null);
verify(controller).onExportDB();
}
@Test
public void testOnResult_importData()
{
screen.onResult(0, ListHabitsScreen.RESULT_IMPORT_DATA, null);
screen.onResult(REQUEST_SETTINGS, RESULT_IMPORT_DATA, null);
testShowImportScreen();
}

@ -1,7 +1,9 @@
dependencies:
pre:
- echo y | android update sdk --no-ui --all --filter "tools"
- echo y | android update sdk --no-ui --all --filter "build-tools-23.0.3"
- echo y | android update sdk --no-ui --all --filter "android-25"
- echo y | android update sdk --no-ui --all --filter "build-tools-25.0.2"
- echo y | android update sdk --no-ui --all --filter "extra-android-m2repository"
checkout:
post:
- git submodule sync

Loading…
Cancel
Save