Initial support for creating tasks via Tasker

pull/618/head
Alex Baker 7 years ago
parent 169c7564d5
commit b5d9722220

@ -130,6 +130,7 @@ dependencies {
compile "com.android.support:support-annotations:${SUPPORT_VERSION}"
compile "com.android.support:support-v13:${SUPPORT_VERSION}"
compile "com.android.support:cardview-v7:${SUPPORT_VERSION}"
compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile 'com.jakewharton.timber:timber:4.5.1'
compile 'com.jakewharton.threetenabp:threetenabp:1.0.5'
//noinspection GradleDependency

@ -24,6 +24,7 @@ import org.tasks.activities.TimePickerActivity;
import org.tasks.dashclock.DashClockSettings;
import org.tasks.files.FileExplore;
import org.tasks.files.MyFilePickerActivity;
import org.tasks.locale.ui.activity.TaskerCreateTaskActivity;
import org.tasks.locale.ui.activity.TaskerSettingsActivity;
import org.tasks.preferences.AppearancePreferences;
import org.tasks.preferences.BasicPreferences;
@ -120,4 +121,6 @@ public interface ActivityComponent {
void inject(BasicPreferences basicPreferences);
void inject(GoogleTaskListSettingsActivity googleTaskListSettingsActivity);
void inject(TaskerCreateTaskActivity taskerCreateTaskActivity);
}

@ -24,6 +24,7 @@ import org.tasks.activities.TimePickerActivity;
import org.tasks.dashclock.DashClockSettings;
import org.tasks.files.FileExplore;
import org.tasks.files.MyFilePickerActivity;
import org.tasks.locale.ui.activity.TaskerCreateTaskActivity;
import org.tasks.locale.ui.activity.TaskerSettingsActivity;
import org.tasks.preferences.AppearancePreferences;
import org.tasks.preferences.BasicPreferences;
@ -120,4 +121,6 @@ public interface ActivityComponent {
void inject(BasicPreferences basicPreferences);
void inject(GoogleTaskListSettingsActivity googleTaskListSettingsActivity);
void inject(TaskerCreateTaskActivity taskerCreateTaskActivity);
}

@ -26,6 +26,7 @@ import org.tasks.activities.TimePickerActivity;
import org.tasks.dashclock.DashClockSettings;
import org.tasks.files.FileExplore;
import org.tasks.files.MyFilePickerActivity;
import org.tasks.locale.ui.activity.TaskerCreateTaskActivity;
import org.tasks.locale.ui.activity.TaskerSettingsActivity;
import org.tasks.preferences.AppearancePreferences;
import org.tasks.preferences.BasicPreferences;
@ -127,4 +128,6 @@ public interface ActivityComponent {
void inject(BasicPreferences basicPreferences);
void inject(GoogleTaskListSettingsActivity googleTaskListSettingsActivity);
void inject(TaskerCreateTaskActivity taskerCreateTaskActivity);
}

@ -466,6 +466,11 @@
<receiver android:name=".notifications.NotificationClearedReceiver" />
<service
android:name=".locale.receiver.TaskerIntentService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="false" />
<!-- Uses Library -->
<uses-library
android:name="com.google.android.maps"
@ -495,10 +500,25 @@
<!-- Tasker/Locale -->
<activity
android:name=".locale.ui.activity.TaskerCreateTaskActivity"
android:exported="true"
android:enabled="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/tasker_create_task"
android:theme="@style/Tasks"
android:uiOptions="splitActionBarWhenNarrow"
android:windowSoftInputMode="adjustResize"
tools:ignore="ExportedActivity">
<intent-filter>
<action android:name="com.twofortyfouram.locale.intent.action.EDIT_SETTING" />
</intent-filter>
</activity>
<activity
android:name=".locale.ui.activity.TaskerSettingsActivity"
android:exported="false"
android:label="@string/app_name"
android:label="@string/tasker_list_notification"
android:theme="@style/Tasks"
android:uiOptions="splitActionBarWhenNarrow"
android:windowSoftInputMode="adjustResize"/>
@ -508,7 +528,7 @@
android:exported="true"
android:enabled="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:label="@string/tasker_list_notification"
android:targetActivity=".locale.ui.activity.TaskerSettingsActivity"
tools:ignore="ExportedActivity">
<intent-filter>
@ -520,7 +540,6 @@
android:name=".locale.receiver.FireReceiver"
android:exported="true"
android:enabled="true"
android:process=":background"
tools:ignore="ExportedReceiver">
<intent-filter>
<action android:name="com.twofortyfouram.locale.intent.action.FIRE_SETTING"/>

@ -0,0 +1,218 @@
//package com.yourcompany.yourcondition;
//package com.yourcompany.yoursetting;
package net.dinglisch.android.tasker;
// Constants and functions for Tasker *extensions* to the plugin protocol
// See Also: http://tasker.dinglisch.net/plugins.html
// Release Notes
// v1.1 20140202
// added function variableNameValid()
// fixed some javadoc entries (thanks to David Stone)
// v1.2 20140211
// added ACTION_EDIT_EVENT
// v1.3 20140227
// added REQUESTED_TIMEOUT_MS_NONE, REQUESTED_TIMEOUT_MS_MAX and REQUESTED_TIMEOUT_MS_NEVER
// requestTimeoutMS(): added range check
// v1.4 20140516
// support for data pass through in REQUEST_QUERY intent
// some javadoc entries fixed (thanks again David :-))
// v1.5 20141120
// added RESULT_CODE_FAILED_PLUGIN_FIRST
// added Setting.VARNAME_ERROR_MESSAGE
// v1.6 20150213
// added Setting.getHintTimeoutMS()
// added Host.addHintTimeoutMS()
// v1.7 20160619
// null check for getCallingActivity() in hostSupportsOnFireVariableReplacement( Activity editActivity )
// v1.8 20161002
// added hostSupportsKeyEncoding(), setKeyEncoding() and Host.getKeysWithEncoding()
import android.app.Activity;
import android.content.ComponentName;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.util.Log;
public class TaskerPlugin {
private final static String TAG = "TaskerPlugin";
private final static String BASE_KEY = "net.dinglisch.android.tasker";
private final static String EXTRAS_PREFIX = BASE_KEY + ".extras.";
private final static int FIRST_ON_FIRE_VARIABLES_TASKER_VERSION = 80;
/**
* Host capabilities, passed to plugin with edit intents
*/
private final static String EXTRA_HOST_CAPABILITIES = EXTRAS_PREFIX + "HOST_CAPABILITIES";
/**
* @see Setting#hostSupportsOnFireVariableReplacement(Bundle)
*/
public final static int EXTRA_HOST_CAPABILITY_SETTING_FIRE_VARIABLE_REPLACEMENT = 8;
/**
* Possible encodings of text in bundle values
*
* @see #setKeyEncoding(Bundle,String[],Encoding)
*/
public enum Encoding { JSON };
// ----------------------------- SETTING PLUGIN ONLY --------------------------------- //
public static class Setting {
/**
* @see #setVariableReplaceKeys(Bundle, String[])
*/
private final static String BUNDLE_KEY_VARIABLE_REPLACE_STRINGS = EXTRAS_PREFIX + "VARIABLE_REPLACE_KEYS";
/**
* Used by: plugin EditActivity.
*
* Indicates to plugin that host will replace variables in specified bundle keys.
*
* Replacement takes place every time the setting is fired, before the bundle is
* passed to the plugin FireReceiver.
*
* @param extrasFromHost intent extras from the intent received by the edit activity
* @see #setVariableReplaceKeys(Bundle, String[])
*/
public static boolean hostSupportsOnFireVariableReplacement( Bundle extrasFromHost ) {
return hostSupports( extrasFromHost, EXTRA_HOST_CAPABILITY_SETTING_FIRE_VARIABLE_REPLACEMENT );
}
/**
* Used by: plugin EditActivity.
*
* Description as above.
*
* This version also includes backwards compatibility with pre 4.2 Tasker versions.
* At some point this function will be deprecated.
*
* @param editActivity the plugin edit activity, needed to test calling Tasker version
* @see #setVariableReplaceKeys(Bundle, String[])
*/
public static boolean hostSupportsOnFireVariableReplacement( Activity editActivity ) {
boolean supportedFlag = hostSupportsOnFireVariableReplacement( editActivity.getIntent().getExtras() );
if ( ! supportedFlag ) {
ComponentName callingActivity = editActivity.getCallingActivity();
if ( callingActivity == null )
Log.w( TAG, "hostSupportsOnFireVariableReplacement: null callingActivity, defaulting to false" );
else {
String callerPackage = callingActivity.getPackageName();
// Tasker only supporteed this from 1.0.10
supportedFlag =
( callerPackage.startsWith( BASE_KEY ) ) &&
( getPackageVersionCode( editActivity.getPackageManager(), callerPackage ) > FIRST_ON_FIRE_VARIABLES_TASKER_VERSION )
;
}
}
return supportedFlag;
}
/**
* Used by: plugin EditActivity
*
* Indicates to host which bundle keys should be replaced.
*
* @param resultBundleToHost the bundle being returned to the host
* @param listOfKeyNames which bundle keys to replace variables in when setting fires
* @see #hostSupportsOnFireVariableReplacement(Bundle)
* @see #setKeyEncoding(Bundle,String[],Encoding)
*/
public static void setVariableReplaceKeys( Bundle resultBundleToHost, String [] listOfKeyNames ) {
addStringArrayToBundleAsString(
listOfKeyNames, resultBundleToHost, BUNDLE_KEY_VARIABLE_REPLACE_STRINGS,
"setVariableReplaceKeys"
);
}
}
// ---------------------------------- HELPER FUNCTIONS -------------------------------- //
private static Object getBundleValueSafe( Bundle b, String key, Class<?> expectedClass, String funcName ) {
Object value = null;
if ( b != null ) {
if ( b.containsKey( key ) ) {
Object obj = b.get( key );
if ( obj == null )
Log.w( TAG, funcName + ": " + key + ": null value" );
else if ( obj.getClass() != expectedClass )
Log.w( TAG, funcName + ": " + key + ": expected " + expectedClass.getClass().getName() + ", got " + obj.getClass().getName() );
else
value = obj;
}
}
return value;
}
private static boolean hostSupports( Bundle extrasFromHost, int capabilityFlag ) {
Integer flags = (Integer) getBundleValueSafe( extrasFromHost, EXTRA_HOST_CAPABILITIES, Integer.class, "hostSupports" );
return
( flags != null ) &&
( ( flags & capabilityFlag ) > 0 )
;
}
public static int getPackageVersionCode( PackageManager pm, String packageName ) {
int code = -1;
if ( pm != null ) {
try {
PackageInfo pi = pm.getPackageInfo( packageName, 0 );
if ( pi != null )
code = pi.versionCode;
}
catch ( Exception e ) {
Log.e( TAG, "getPackageVersionCode: exception getting package info" );
}
}
return code;
}
private static void addStringArrayToBundleAsString( String [] toAdd, Bundle bundle, String key, String callerName ) {
StringBuilder builder = new StringBuilder();
if ( toAdd != null ) {
for ( String keyName : toAdd ) {
if ( keyName.contains( " " ) )
Log.w( TAG, callerName + ": ignoring bad keyName containing space: " + keyName );
else {
if ( builder.length() > 0 )
builder.append( ' ' );
builder.append( keyName );
}
if ( builder.length() > 0 )
bundle.putString( key, builder.toString() );
}
}
}
}

@ -31,7 +31,9 @@ public class Tracking {
SET_PREFERENCE(R.string.tracking_category_preferences, 0),
PLAY_SERVICES_WARNING(R.string.tracking_category_event, R.string.tracking_event_play_services_error),
RECURRENCE_CUSTOM(R.string.tracking_category_recurrence, R.string.tracking_action_custom),
RECURRENCE_PRESET(R.string.tracking_category_recurrence, R.string.tracking_action_preset);
RECURRENCE_PRESET(R.string.tracking_category_recurrence, R.string.tracking_action_preset),
TASKER_CREATE(R.string.tracking_category_tasker, R.string.tracking_action_task_created),
TASKER_LIST_NOTIFICATION(R.string.tracking_category_tasker, R.string.tracking_action_list_notification);
public final int category;
public final int action;

@ -1,10 +1,11 @@
package org.tasks.injection;
import org.tasks.jobs.NotificationJob;
import org.tasks.jobs.AfterSaveIntentService;
import org.tasks.jobs.BackupJob;
import org.tasks.jobs.MidnightRefreshJob;
import org.tasks.jobs.NotificationJob;
import org.tasks.jobs.RefreshJob;
import org.tasks.jobs.AfterSaveIntentService;
import org.tasks.locale.receiver.TaskerIntentService;
import org.tasks.location.GeofenceTransitionsIntentService;
import org.tasks.scheduling.BackgroundScheduler;
import org.tasks.scheduling.CalendarNotificationIntentService;
@ -34,4 +35,6 @@ public interface IntentServiceComponent {
void inject(BackgroundScheduler backgroundScheduler);
void inject(AfterSaveIntentService afterSaveIntentService);
void inject(TaskerIntentService taskerIntentService);
}

@ -29,6 +29,7 @@ public class JobManager {
public static final int JOB_ID_TASK_STATUS_CHANGE = 8;
public static final int JOB_ID_NOTIFICATION_SCHEDULER = 9;
public static final int JOB_ID_CALENDAR_NOTIFICATION = 10;
public static final int JOB_ID_TASKER = 11;
private Context context;
private AlarmManager alarmManager;

@ -6,7 +6,9 @@ import org.tasks.BuildConfig;
import timber.log.Timber;
public final class PluginBundleValues {
import static com.google.common.base.Strings.isNullOrEmpty;
public final class ListNotificationBundle {
public static final String BUNDLE_EXTRA_STRING_FILTER = "org.tasks.locale.STRING_FILTER";
public static final String BUNDLE_EXTRA_PREVIOUS_BUNDLE = "org.tasks.locale.PREVIOUS_BUNDLE";
@ -18,28 +20,18 @@ public final class PluginBundleValues {
return false;
}
if (isNullOrEmpty(bundle, BUNDLE_EXTRA_STRING_FILTER)) {
if (bundle.getInt(BUNDLE_EXTRA_INT_VERSION_CODE, -1) == -1) {
return false;
}
Integer version = bundle.getInt(BUNDLE_EXTRA_INT_VERSION_CODE, -1);
if (version == -1) {
Timber.e("invalid version code: %s", version);
if (isNullOrEmpty(bundle.getString(BUNDLE_EXTRA_STRING_FILTER))) {
Timber.e("Invalid %s", BUNDLE_EXTRA_STRING_FILTER);
return false;
}
return true;
}
private static boolean isNullOrEmpty(Bundle bundle, String key) {
String value = bundle.getString(key);
boolean isNullOrEmpty = value == null || value.trim().length() == 0;
if (isNullOrEmpty) {
Timber.e("Invalid %s", key);
}
return isNullOrEmpty;
}
public static Bundle generateBundle(String filter) {
Bundle result = new Bundle();
result.putInt(BUNDLE_EXTRA_INT_VERSION_CODE, BuildConfig.VERSION_CODE);
@ -51,6 +43,6 @@ public final class PluginBundleValues {
return bundle.getString(BUNDLE_EXTRA_STRING_FILTER);
}
private PluginBundleValues() {
private ListNotificationBundle() {
}
}

@ -0,0 +1,87 @@
package org.tasks.locale.bundle;
import android.os.Bundle;
import org.tasks.BuildConfig;
public class TaskCreationBundle {
public static final String EXTRA_BUNDLE = "org.tasks.locale.create";
public static final String EXTRA_TITLE = "org.tasks.locale.create.STRING_TITLE";
public static final String EXTRA_DUE_DATE = "org.tasks.locale.create.STRING_DUE_DATE";
public static final String EXTRA_DUE_TIME = "org.tasks.locale.create.STRING_DUE_TIME";
public static final String EXTRA_PRIORITY = "org.tasks.locale.create.STRING_PRIORITY";
public static final String EXTRA_DESCRIPTION = "org.tasks.locale.create.STRING_DESCRIPTION";
public static final String EXTRA_VERSION_CODE = "org.tasks.locale.create.INT_VERSION_CODE";
private final Bundle bundle;
public TaskCreationBundle() {
this(null);
}
public TaskCreationBundle(Bundle bundle) {
if (bundle == null) {
this.bundle = new Bundle();
this.bundle.putInt(EXTRA_VERSION_CODE, BuildConfig.VERSION_CODE);
} else {
this.bundle = bundle;
}
}
public static boolean isBundleValid(Bundle bundle) {
return -1 != bundle.getInt(EXTRA_VERSION_CODE, -1);
}
public void setTitle(String title) {
bundle.putString(EXTRA_TITLE, title);
}
public String getTitle() {
return bundle.getString(EXTRA_TITLE);
}
public void setDueDate(String dueDate) {
bundle.putString(EXTRA_DUE_DATE, dueDate);
}
public String getDueDate() {
return bundle.getString(EXTRA_DUE_DATE);
}
public void setDueTime(String dueTime) {
bundle.putString(EXTRA_DUE_TIME, dueTime);
}
public String getDueTime() {
return bundle.getString(EXTRA_DUE_TIME);
}
public void setPriority(String priority) {
bundle.putString(EXTRA_PRIORITY, priority);
}
public String getPriority() {
return bundle.getString(EXTRA_PRIORITY);
}
public void setDescription(String description) {
bundle.putString(EXTRA_DESCRIPTION, description);
}
public String getDescription() {
return bundle.getString(EXTRA_DESCRIPTION);
}
public Bundle build() {
bundle.putInt(EXTRA_VERSION_CODE, BuildConfig.VERSION_CODE);
return bundle;
}
@Override
public String toString() {
return "TaskCreationBundle{" +
"bundle=" + bundle +
'}';
}
}

@ -1,31 +1,19 @@
package org.tasks.locale.receiver;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.JobIntentService;
import com.todoroo.astrid.api.Filter;
import org.tasks.Notifier;
import org.tasks.injection.BroadcastComponent;
import org.tasks.injection.InjectingBroadcastReceiver;
import org.tasks.locale.bundle.PluginBundleValues;
import org.tasks.preferences.DefaultFilterProvider;
import javax.inject.Inject;
import org.tasks.jobs.JobManager;
import timber.log.Timber;
public final class FireReceiver extends InjectingBroadcastReceiver {
@Inject Notifier notifier;
@Inject DefaultFilterProvider defaultFilterProvider;
public final class FireReceiver extends BroadcastReceiver {
@Override
public final void onReceive(final Context context, final Intent intent) {
super.onReceive(context, intent);
Timber.d("Received %s", intent); //$NON-NLS-1$
/*
@ -54,28 +42,6 @@ public final class FireReceiver extends InjectingBroadcastReceiver {
return;
}
final Bundle bundle = intent
.getBundleExtra(com.twofortyfouram.locale.api.Intent.EXTRA_BUNDLE);
if (null == bundle) {
Timber.e("%s is missing",
com.twofortyfouram.locale.api.Intent.EXTRA_BUNDLE); //$NON-NLS-1$
return;
}
if (!PluginBundleValues.isBundleValid(bundle)) {
Timber.e("%s is invalid",
com.twofortyfouram.locale.api.Intent.EXTRA_BUNDLE); //$NON-NLS-1$
return;
}
Filter filter = defaultFilterProvider.getFilterFromPreference(
bundle.getString(PluginBundleValues.BUNDLE_EXTRA_STRING_FILTER));
notifier.triggerFilterNotification(filter);
}
@Override
protected void inject(BroadcastComponent component) {
component.inject(this);
JobIntentService.enqueueWork(context, TaskerIntentService.class, JobManager.JOB_ID_TASKER, intent);
}
}

@ -0,0 +1,62 @@
package org.tasks.locale.receiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import com.todoroo.astrid.api.Filter;
import org.tasks.Notifier;
import org.tasks.analytics.Tracker;
import org.tasks.analytics.Tracking;
import org.tasks.injection.ForApplication;
import org.tasks.injection.InjectingJobIntentService;
import org.tasks.injection.IntentServiceComponent;
import org.tasks.locale.bundle.ListNotificationBundle;
import org.tasks.locale.bundle.TaskCreationBundle;
import org.tasks.preferences.DefaultFilterProvider;
import javax.inject.Inject;
import timber.log.Timber;
public class TaskerIntentService extends InjectingJobIntentService {
@Inject @ForApplication Context context;
@Inject Notifier notifier;
@Inject DefaultFilterProvider defaultFilterProvider;
@Inject TaskerTaskCreator taskerTaskCreator;
@Inject Tracker tracker;
@Override
protected void onHandleWork(@NonNull Intent intent) {
super.onHandleWork(intent);
final Bundle bundle = intent
.getBundleExtra(com.twofortyfouram.locale.api.Intent.EXTRA_BUNDLE);
if (null == bundle) {
Timber.e("%s is missing",
com.twofortyfouram.locale.api.Intent.EXTRA_BUNDLE); //$NON-NLS-1$
return;
}
if (ListNotificationBundle.isBundleValid(bundle)) {
Filter filter = defaultFilterProvider.getFilterFromPreference(
bundle.getString(ListNotificationBundle.BUNDLE_EXTRA_STRING_FILTER));
notifier.triggerFilterNotification(filter);
tracker.reportEvent(Tracking.Events.TASKER_LIST_NOTIFICATION);
} else if (TaskCreationBundle.isBundleValid(bundle)) {
taskerTaskCreator.handle(new TaskCreationBundle(bundle));
tracker.reportEvent(Tracking.Events.TASKER_CREATE);
} else {
Timber.e("Invalid bundle: %s", bundle);
}
}
@Override
protected void inject(IntentServiceComponent component) {
component.inject(this);
}
}

@ -0,0 +1,76 @@
package org.tasks.locale.receiver;
import com.todoroo.astrid.dao.TaskDao;
import com.todoroo.astrid.data.Task;
import com.todoroo.astrid.service.TaskCreator;
import org.tasks.locale.bundle.TaskCreationBundle;
import org.tasks.time.DateTime;
import org.threeten.bp.LocalDate;
import org.threeten.bp.LocalTime;
import org.threeten.bp.format.DateTimeFormatter;
import javax.inject.Inject;
import timber.log.Timber;
import static com.google.common.base.Strings.isNullOrEmpty;
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
public class TaskerTaskCreator {
private static final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("MM-dd-yy");
private static final DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH.mm");
private final TaskCreator taskCreator;
private final TaskDao taskDao;
@Inject
public TaskerTaskCreator(TaskCreator taskCreator, TaskDao taskDao) {
this.taskCreator = taskCreator;
this.taskDao = taskDao;
}
public void handle(TaskCreationBundle bundle) {
Task task = taskCreator.createWithValues(null, bundle.getTitle());
String dueDateString = bundle.getDueDate();
if (!isNullOrEmpty(dueDateString)) {
try {
LocalDate dueDate = LocalDate.parse(dueDateString, dateFormatter);
DateTime dt = new DateTime(dueDate.getYear(), dueDate.getMonthValue(), dueDate.getDayOfMonth());
task.setDueDate(Task.createDueDate(Task.URGENCY_SPECIFIC_DAY, dt.getMillis()));
} catch(Exception e) {
Timber.e(e.getMessage(), e);
}
}
String dueTimeString = bundle.getDueTime();
if (!isNullOrEmpty(dueTimeString)) {
try {
LocalTime dueTime = LocalTime.parse(dueTimeString, timeFormatter);
task.setDueDate(Task.createDueDate(Task.URGENCY_SPECIFIC_DAY_TIME,
new DateTime(task.hasDueDate() ? task.getDueDate() : currentTimeMillis())
.withHourOfDay(dueTime.getHour())
.withMinuteOfHour(dueTime.getMinute())
.getMillis()));
} catch (Exception e) {
Timber.e(e.getMessage(), e);
}
}
String priorityString = bundle.getPriority();
if (!isNullOrEmpty(priorityString)) {
try {
int priority = Integer.parseInt(priorityString);
task.setImportance(Math.max(0, Math.min(3, priority)));
} catch (NumberFormatException e) {
Timber.e(e.getMessage(), e);
}
}
task.setNotes(bundle.getDescription());
taskDao.save(task);
}
}

@ -3,11 +3,15 @@ package org.tasks.locale.ui.activity;
import android.content.Intent;
import android.os.Bundle;
import com.todoroo.andlib.utility.AndroidUtilities;
import org.tasks.injection.InjectingAppCompatActivity;
import org.tasks.injection.InjectingPreferenceActivity;
import org.tasks.injection.ThemedInjectingAppCompatActivity;
import timber.log.Timber;
public abstract class AbstractFragmentPluginAppCompatActivity extends InjectingPreferenceActivity {
public abstract class AbstractFragmentPluginAppCompatActivity extends ThemedInjectingAppCompatActivity {
boolean mIsCancelled = false;
@ -47,6 +51,7 @@ public abstract class AbstractFragmentPluginAppCompatActivity extends InjectingP
@Override
public void finish() {
AndroidUtilities.hideKeyboard(this);
if (isLocalePluginIntent(getIntent())) {
if (!mIsCancelled) {
final Bundle resultBundle = getResultBundle();

@ -0,0 +1,140 @@
package org.tasks.locale.ui.activity;
import android.content.Intent;
import android.os.Bundle;
import org.tasks.injection.InjectingPreferenceActivity;
import timber.log.Timber;
public abstract class AbstractFragmentPluginPreferenceActivity extends InjectingPreferenceActivity {
boolean mIsCancelled = false;
/* package */ private static boolean isLocalePluginIntent(final Intent intent) {
final String action = intent.getAction();
return com.twofortyfouram.locale.api.Intent.ACTION_EDIT_CONDITION.equals(action)
|| com.twofortyfouram.locale.api.Intent.ACTION_EDIT_SETTING.equals(action);
}
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (isLocalePluginIntent(getIntent())) {
final Bundle previousBundle = getPreviousBundle();
Timber.d("Creating Activity with Intent=%s, savedInstanceState=%s, EXTRA_BUNDLE=%s",
getIntent(), savedInstanceState, previousBundle); //$NON-NLS-1$
}
}
@Override
protected void onPostCreate(final Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
if (isLocalePluginIntent(getIntent())) {
if (null == savedInstanceState) {
final Bundle previousBundle = getPreviousBundle();
final String previousBlurb = getPreviousBlurb();
if (null != previousBundle && null != previousBlurb) {
onPostCreateWithPreviousResult(previousBundle, previousBlurb);
}
}
}
}
@Override
public void finish() {
if (isLocalePluginIntent(getIntent())) {
if (!mIsCancelled) {
final Bundle resultBundle = getResultBundle();
if (null != resultBundle) {
String blurb = getResultBlurb(resultBundle);
Intent resultIntent = new Intent();
resultIntent.putExtra(com.twofortyfouram.locale.api.Intent.EXTRA_BUNDLE,
resultBundle);
resultIntent.putExtra(com.twofortyfouram.locale.api.Intent.EXTRA_STRING_BLURB,
blurb);
setResult(RESULT_OK, resultIntent);
}
}
}
super.finish();
}
/**
* @return The {@link com.twofortyfouram.locale.api.Intent#EXTRA_BUNDLE EXTRA_BUNDLE} that was
* previously saved to the host and subsequently passed back to this Activity for further
* editing. Internally, this method relies on {@link #isBundleValid(Bundle)}. If
* the bundle exists but is not valid, this method will return null.
*/
private Bundle getPreviousBundle() {
final Bundle bundle = getIntent().getBundleExtra(
com.twofortyfouram.locale.api.Intent.EXTRA_BUNDLE);
if (null != bundle) {
if (isBundleValid(bundle)) {
return bundle;
}
}
return null;
}
/**
* @return The {@link com.twofortyfouram.locale.api.Intent#EXTRA_STRING_BLURB
* EXTRA_STRING_BLURB} that was
* previously saved to the host and subsequently passed back to this Activity for further
* editing.
*/
private String getPreviousBlurb() {
return getIntent().getStringExtra(
com.twofortyfouram.locale.api.Intent.EXTRA_STRING_BLURB);
}
/**
* <p>Validates the Bundle, to ensure that a malicious application isn't attempting to pass
* an invalid Bundle.</p>
*
* @param bundle The plug-in's Bundle previously returned by the edit
* Activity. {@code bundle} should not be mutated by this method.
* @return true if {@code bundle} is valid for the plug-in.
*/
protected abstract boolean isBundleValid(final Bundle bundle);
/**
* Plug-in Activity lifecycle callback to allow the Activity to restore
* state for editing a previously saved plug-in instance. This callback will
* occur during the onPostCreate() phase of the Activity lifecycle.
* <p>{@code bundle} will have been
* validated by {@link #isBundleValid(Bundle)} prior to this
* method being called. If {@link #isBundleValid(Bundle)} returned false, then this
* method will not be called. This helps ensure that plug-in Activity subclasses only have to
* worry about bundle validation once, in the {@link #isBundleValid(Bundle)}
* method.</p>
* <p>Note this callback only occurs the first time the Activity is created, so it will not be
* called
* when the Activity is recreated (e.g. {@code savedInstanceState != null}) such as after a
* configuration change like a screen rotation.</p>
*
* @param previousBundle Previous bundle that the Activity saved.
* @param previousBlurb Previous blurb that the Activity saved
*/
protected abstract void onPostCreateWithPreviousResult(
final Bundle previousBundle, final String previousBlurb);
/**
* @return Bundle for the plug-in or {@code null} if a valid Bundle cannot
* be generated.
*/
protected abstract Bundle getResultBundle();
/**
* @param bundle Valid bundle for the component.
* @return Blurb for {@code bundle}.
*/
protected abstract String getResultBlurb(final Bundle bundle);
}

@ -0,0 +1,189 @@
package org.tasks.locale.ui.activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.design.widget.TextInputEditText;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import net.dinglisch.android.tasker.TaskerPlugin;
import org.tasks.R;
import org.tasks.billing.PurchaseHelper;
import org.tasks.billing.PurchaseHelperCallback;
import org.tasks.injection.ActivityComponent;
import org.tasks.locale.bundle.TaskCreationBundle;
import org.tasks.preferences.Preferences;
import org.tasks.ui.MenuColorizer;
import javax.inject.Inject;
import butterknife.BindView;
import butterknife.ButterKnife;
public final class TaskerCreateTaskActivity extends AbstractFragmentPluginAppCompatActivity implements PurchaseHelperCallback, Toolbar.OnMenuItemClickListener {
private static final int REQUEST_PURCHASE = 10125;
private static final String EXTRA_PURCHASE_INITIATED = "extra_purchase_initiated";
@Inject Preferences preferences;
@Inject PurchaseHelper purchaseHelper;
@BindView(R.id.title) TextInputEditText title;
@BindView(R.id.toolbar) Toolbar toolbar;
@BindView(R.id.due_date) TextInputEditText dueDate;
@BindView(R.id.due_time) TextInputEditText dueTime;
@BindView(R.id.priority) TextInputEditText priority;
@BindView(R.id.description) TextInputEditText description;
private Bundle previousBundle;
private boolean purchaseInitiated;
@Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tasker_create);
ButterKnife.bind(this);
toolbar.setTitle(R.string.tasker_create_task);
final boolean backButtonSavesTask = preferences.backButtonSavesTask();
toolbar.setNavigationIcon(ContextCompat.getDrawable(this,
backButtonSavesTask ? R.drawable.ic_close_24dp : R.drawable.ic_save_24dp));
toolbar.setNavigationOnClickListener(v -> {
if (backButtonSavesTask) {
discardButtonClick();
} else {
save();
}
});
toolbar.setOnMenuItemClickListener(this);
toolbar.inflateMenu(R.menu.menu_tasker_create_task);
MenuColorizer.colorToolbar(this, toolbar);
if (savedInstanceState != null) {
previousBundle = savedInstanceState.getParcelable(TaskCreationBundle.EXTRA_BUNDLE);
purchaseInitiated = savedInstanceState.getBoolean(EXTRA_PURCHASE_INITIATED);
TaskCreationBundle bundle = new TaskCreationBundle(previousBundle);
title.setText(bundle.getTitle());
}
if (!preferences.hasPurchase(R.string.p_purchased_tasker) && !purchaseInitiated) {
purchaseInitiated = purchaseHelper.purchase(this, getString(R.string.sku_tasker), getString(R.string.p_purchased_tasker), REQUEST_PURCHASE, this);
}
}
@Override
public void onPostCreateWithPreviousResult(final Bundle previousBundle, final String previousBlurb) {
this.previousBundle = previousBundle;
TaskCreationBundle bundle = new TaskCreationBundle(previousBundle);
title.setText(bundle.getTitle());
dueDate.setText(bundle.getDueDate());
dueTime.setText(bundle.getDueTime());
priority.setText(bundle.getPriority());
description.setText(bundle.getDescription());
}
@Override
public boolean isBundleValid(final Bundle bundle) {
return TaskCreationBundle.isBundleValid(bundle);
}
@Override
protected Bundle getResultBundle() {
TaskCreationBundle bundle = new TaskCreationBundle();
bundle.setTitle(title.getText().toString().trim());
bundle.setDueDate(dueDate.getText().toString().trim());
bundle.setDueTime(dueTime.getText().toString().trim());
bundle.setPriority(priority.getText().toString().trim());
bundle.setDescription(description.getText().toString().trim());
Bundle resultBundle = bundle.build();
if (TaskerPlugin.Setting.hostSupportsOnFireVariableReplacement(this)) {
TaskerPlugin.Setting.setVariableReplaceKeys(resultBundle, new String[] {
TaskCreationBundle.EXTRA_TITLE,
TaskCreationBundle.EXTRA_DUE_DATE,
TaskCreationBundle.EXTRA_DUE_TIME,
TaskCreationBundle.EXTRA_PRIORITY,
TaskCreationBundle.EXTRA_DESCRIPTION
});
}
return resultBundle;
}
@Override
public String getResultBlurb(final Bundle bundle) {
return title.getText().toString().trim();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_PURCHASE) {
purchaseHelper.handleActivityResult(this, requestCode, resultCode, data);
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
@Override
public void onBackPressed() {
final boolean backButtonSavesTask = preferences.backButtonSavesTask();
if (backButtonSavesTask) {
save();
} else {
discardButtonClick();
}
}
private void save() {
finish();
}
private void discardButtonClick() {
mIsCancelled = true;
finish();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (!isChangingConfigurations()) {
purchaseHelper.disposeIabHelper();
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(TaskCreationBundle.EXTRA_BUNDLE, previousBundle);
outState.putBoolean(EXTRA_PURCHASE_INITIATED, purchaseInitiated);
}
@Override
public void inject(ActivityComponent component) {
component.inject(this);
}
@Override
public void purchaseCompleted(boolean success, String sku) {
if (!success) {
discardButtonClick();
}
}
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_save:
save();
return true;
case R.id.menu_help:
startActivity(new Intent(Intent.ACTION_VIEW).setData(Uri.parse("http://tasks.org/help")));
return true;
}
return super.onOptionsItemSelected(item);
}
}

@ -12,13 +12,13 @@ import org.tasks.activities.FilterSelectionActivity;
import org.tasks.billing.PurchaseHelper;
import org.tasks.billing.PurchaseHelperCallback;
import org.tasks.injection.ActivityComponent;
import org.tasks.locale.bundle.PluginBundleValues;
import org.tasks.locale.bundle.ListNotificationBundle;
import org.tasks.preferences.DefaultFilterProvider;
import org.tasks.preferences.Preferences;
import javax.inject.Inject;
public final class TaskerSettingsActivity extends AbstractFragmentPluginAppCompatActivity implements PurchaseHelperCallback, Toolbar.OnMenuItemClickListener {
public final class TaskerSettingsActivity extends AbstractFragmentPluginPreferenceActivity implements PurchaseHelperCallback, Toolbar.OnMenuItemClickListener {
private static final int REQUEST_SELECT_FILTER = 10124;
private static final int REQUEST_PURCHASE = 10125;
@ -40,7 +40,7 @@ public final class TaskerSettingsActivity extends AbstractFragmentPluginAppCompa
addPreferencesFromResource(R.xml.preferences_tasker);
if (savedInstanceState != null) {
previousBundle = savedInstanceState.getParcelable(PluginBundleValues.BUNDLE_EXTRA_PREVIOUS_BUNDLE);
previousBundle = savedInstanceState.getParcelable(ListNotificationBundle.BUNDLE_EXTRA_PREVIOUS_BUNDLE);
filter = savedInstanceState.getParcelable(EXTRA_FILTER);
purchaseInitiated = savedInstanceState.getBoolean(EXTRA_PURCHASE_INITIATED);
} else {
@ -64,18 +64,18 @@ public final class TaskerSettingsActivity extends AbstractFragmentPluginAppCompa
@Override
public void onPostCreateWithPreviousResult(final Bundle previousBundle, final String previousBlurb) {
this.previousBundle = previousBundle;
this.filter = defaultFilterProvider.getFilterFromPreference(PluginBundleValues.getFilter(previousBundle));
this.filter = defaultFilterProvider.getFilterFromPreference(ListNotificationBundle.getFilter(previousBundle));
refreshPreferences();
}
@Override
public boolean isBundleValid(final Bundle bundle) {
return PluginBundleValues.isBundleValid(bundle);
return ListNotificationBundle.isBundleValid(bundle);
}
@Override
protected Bundle getResultBundle() {
return PluginBundleValues.generateBundle(defaultFilterProvider.getFilterPreferenceValue(filter));
return ListNotificationBundle.generateBundle(defaultFilterProvider.getFilterPreferenceValue(filter));
}
@Override
@ -114,7 +114,7 @@ public final class TaskerSettingsActivity extends AbstractFragmentPluginAppCompa
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable(PluginBundleValues.BUNDLE_EXTRA_PREVIOUS_BUNDLE, previousBundle);
outState.putParcelable(ListNotificationBundle.BUNDLE_EXTRA_PREVIOUS_BUNDLE, previousBundle);
outState.putParcelable(EXTRA_FILTER, filter);
outState.putBoolean(EXTRA_PURCHASE_INITIATED, purchaseInitiated);
}

@ -0,0 +1,112 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/asContentBackground"
android:orientation="vertical">
<include layout="@layout/toolbar" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.design.widget.TextInputLayout
android:id="@+id/textInputLayout"
android:layout_width="368dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<android.support.design.widget.TextInputEditText
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/TEA_title_hint"
android:inputType="textCapSentences|textAutoCorrect" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id="@+id/textInputLayout2"
android:layout_width="368dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textInputLayout">
<android.support.design.widget.TextInputEditText
android:id="@+id/due_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/due_date" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id="@+id/textInputLayout3"
android:layout_width="368dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textInputLayout2">
<android.support.design.widget.TextInputEditText
android:id="@+id/due_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/due_time" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id="@+id/textInputLayout4"
android:layout_width="368dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textInputLayout3">
<android.support.design.widget.TextInputEditText
android:id="@+id/priority"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/TEA_importance_label" />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:layout_width="368dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textInputLayout4">
<android.support.design.widget.TextInputEditText
android:id="@+id/description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/TEA_note_label" />
</android.support.design.widget.TextInputLayout>
</android.support.constraint.ConstraintLayout>
</ScrollView>
</LinearLayout>

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tasks="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_help"
android:icon="@drawable/ic_help_24dp"
android:title="@string/help"
tasks:showAsAction="ifRoom"/>
</menu>

@ -230,6 +230,7 @@
<string name="tracking_category_recurrence">Recurrence</string>
<string name="tracking_category_google_tasks">Gtask</string>
<string name="tracking_category_event">Event</string>
<string name="tracking_category_tasker">Tasker</string>
<string name="tracking_action_add">Add</string>
<string name="tracking_action_start">Start</string>
<string name="tracking_action_move">Move</string>
@ -242,6 +243,8 @@
<string name="tracking_action_clear_completed">Clear completed</string>
<string name="tracking_action_custom">Custom</string>
<string name="tracking_action_preset">Preset</string>
<string name="tracking_action_task_created">Task created</string>
<string name="tracking_action_list_notification">List Notification</string>
<string name="tracking_event_night_mode_mismatch">Night Mismatch</string>
<string name="tracking_event_play_services_error">Play Services Error</string>
<string name="tracking_event_upgrade">Upgrade</string>

@ -872,4 +872,8 @@ File %1$s contained %2$s.\n\n
<string name="repeat_monthly_third_week">third</string>
<string name="repeat_monthly_fourth_week">fourth</string>
<string name="repeat_monthly_last_week">last</string>
<string name="tasker_create_task">Create task</string>
<string name="tasker_list_notification">List notification</string>
<string name="help">Help</string>
</resources>

Loading…
Cancel
Save