diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..e49605eb --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +.git +.idea +.gradle +.settings + +local.properties + +**build/** + +**/*.zip \ No newline at end of file diff --git a/.gitignore b/.gitignore index d5cae053..d1ca3894 100644 --- a/.gitignore +++ b/.gitignore @@ -62,3 +62,11 @@ fastlane/Preview.html fastlane/screenshots fastlane/test_output fastlane/readme.md + +# VS Code +**/.project +**/.settings/ +**/.classpath + +# Exclude local configurations +**/dropbox.xml.idea diff --git a/.idea/misc.xml b/.idea/misc.xml index 39638799..99202cc2 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -5,11 +5,12 @@ diff --git a/.idea/modules.xml b/.idea/modules.xml index e1291e32..6e11e839 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,7 +2,6 @@ - diff --git a/ActionBar-PullToRefresh/.gitignore b/ActionBar-PullToRefresh/.gitignore deleted file mode 100644 index 796b96d1..00000000 --- a/ActionBar-PullToRefresh/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build diff --git a/ActionBar-PullToRefresh/build.gradle b/ActionBar-PullToRefresh/build.gradle deleted file mode 100644 index 6d9a8fe9..00000000 --- a/ActionBar-PullToRefresh/build.gradle +++ /dev/null @@ -1,34 +0,0 @@ -apply plugin: 'com.android.library' - -android { - compileSdkVersion 26 - - - - defaultConfig { - minSdkVersion 21 - targetSdkVersion 26 - 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 fileTree(dir: 'libs', include: ['*.jar']) - - implementation 'com.android.support:appcompat-v7:26.1.0' - testImplementation 'junit:junit:4.12' - androidTestImplementation 'com.android.support.test:runner:1.0.1' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' -} diff --git a/ActionBar-PullToRefresh/proguard-rules.pro b/ActionBar-PullToRefresh/proguard-rules.pro deleted file mode 100644 index f1b42451..00000000 --- a/ActionBar-PullToRefresh/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# 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 diff --git a/ActionBar-PullToRefresh/src/androidTest/java/uk/co/senab/actionbarpulltorefresh/library/ExampleInstrumentedTest.java b/ActionBar-PullToRefresh/src/androidTest/java/uk/co/senab/actionbarpulltorefresh/library/ExampleInstrumentedTest.java deleted file mode 100644 index b3ea4054..00000000 --- a/ActionBar-PullToRefresh/src/androidTest/java/uk/co/senab/actionbarpulltorefresh/library/ExampleInstrumentedTest.java +++ /dev/null @@ -1,26 +0,0 @@ -package uk.co.senab.actionbarpulltorefresh.library; - -import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import static org.junit.Assert.*; - -/** - * Instrumented test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ExampleInstrumentedTest { - @Test - public void useAppContext() throws Exception { - // Context of the app under test. - Context appContext = InstrumentationRegistry.getTargetContext(); - - assertEquals("uk.co.senab.actionbarpulltorefresh.library.test", appContext.getPackageName()); - } -} diff --git a/ActionBar-PullToRefresh/src/main/AndroidManifest.xml b/ActionBar-PullToRefresh/src/main/AndroidManifest.xml deleted file mode 100644 index a74f463a..00000000 --- a/ActionBar-PullToRefresh/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - diff --git a/ActionBar-PullToRefresh/src/main/res/values/strings.xml b/ActionBar-PullToRefresh/src/main/res/values/strings.xml deleted file mode 100644 index 1d8d577e..00000000 --- a/ActionBar-PullToRefresh/src/main/res/values/strings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - ActionBar-PullToRefresh - diff --git a/ActionBar-PullToRefresh/src/test/java/uk/co/senab/actionbarpulltorefresh/library/ExampleUnitTest.java b/ActionBar-PullToRefresh/src/test/java/uk/co/senab/actionbarpulltorefresh/library/ExampleUnitTest.java deleted file mode 100644 index 1ba236ae..00000000 --- a/ActionBar-PullToRefresh/src/test/java/uk/co/senab/actionbarpulltorefresh/library/ExampleUnitTest.java +++ /dev/null @@ -1,17 +0,0 @@ -package uk.co.senab.actionbarpulltorefresh.library; - -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * Example local unit test, which will execute on the development machine (host). - * - * @see Testing documentation - */ -public class ExampleUnitTest { - @Test - public void addition_isCorrect() throws Exception { - assertEquals(4, 2 + 2); - } -} \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..991b6763 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,31 @@ +# docker build -t android-build . + +FROM openjdk:8 + +ENV SDK_URL="https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip" \ + ANDROID_HOME="/usr/local/android-sdk" \ + ANDROID_VERSION=26 \ + ANDROID_BUILD_TOOLS_VERSION=26.0.2 + +WORKDIR /usr/src/app + +RUN mkdir "$ANDROID_HOME" .android \ + && cd "$ANDROID_HOME" \ + && curl -o sdk.zip $SDK_URL \ + && unzip sdk.zip \ + && rm sdk.zip \ + && yes | $ANDROID_HOME/tools/bin/sdkmanager --licenses + +ENV PATH ${PATH}:${ANDROID_HOME}/tools:${ANDROID_HOME}/tools/bin:${ANDROID_HOME}/platform-tools + +RUN sdkmanager --update +RUN sdkmanager "build-tools;${ANDROID_BUILD_TOOLS_VERSION}" \ + "platforms;android-${ANDROID_VERSION}" \ + "platform-tools" + +COPY gradlew build.gradle ./ +COPY gradle/ ./gradle/ + +RUN ./gradlew + +COPY . . \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 5b96f6d8..71cb8b32 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,29 +2,38 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 26 - buildToolsVersion "27.0.3" + compileSdkVersion 28 + buildToolsVersion '28.0.3' defaultConfig { applicationId 'com.todotxt.todotxttouch' - minSdkVersion 14 - targetSdkVersion 17 + minSdkVersion 21 + targetSdkVersion 28 versionCode 35 versionName "release35" + + multiDexEnabled true + } + lintOptions { + checkReleaseBuilds true + // Or, if you prefer, you can continue to check for errors in release builds, + // but continue the build even when errors are found: + abortOnError false + } buildTypes { debug { - resValue "string", "dropbox_consumer_key", "123" - resValue "string", "dropbox_consumer_secret", "456" +// resValue "string", "dropbox_consumer_key", "db-" +// resValue "string", "dropbox_consumer_secret", "" } release { - minifyEnabled true // Enables code shrinking for the release build type. - proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + // minifyEnabled true // Enables code shrinking for the release build type. + // proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } flavorDimensions "free" @@ -63,17 +72,22 @@ android { dependencies { - compile 'com.android.support:support-compat:26.1.0' - compile 'org.apache.james:apache-mime4j-james-utils:0.8.1' - compile 'org.apache.httpcomponents:httpmime:4.5.5' - compile 'com.github.cliftonlabs:json-simple:3.0.2' - compile 'com.googlecode.libphonenumber:libphonenumber:8.9.0' - compile 'oauth.signpost:signpost-commonshttp4:1.2.1.2' - compile 'oauth.signpost:signpost-core:1.2.1.2' - compile 'com.actionbarsherlock:actionbarsherlock:4.4.0@aar' - compile 'com.android.support:support-v4:26.1.0' - compile 'com.dropbox.core:dropbox-core-sdk:3.0.6' - compile fileTree(dir: 'libs', include: ['*.jar']) - testCompile 'junit:junit:4.12' - testCompile 'org.mockito:mockito-core:2.15.0' -} + implementation 'com.android.support:support-compat:28.0.0' + implementation 'com.android.support:preference-v7:28.0.0' + implementation 'com.android.support:preference-v14:28.0.0' + implementation 'com.android.support.constraint:constraint-layout:1.1.2' + implementation 'org.apache.james:apache-mime4j-james-utils:0.8.1' + implementation 'org.apache.httpcomponents:httpmime:4.5.5' + implementation 'com.github.cliftonlabs:json-simple:3.0.2' + implementation 'com.googlecode.libphonenumber:libphonenumber:8.9.0' + implementation 'oauth.signpost:signpost-commonshttp4:1.2.1.2' + implementation 'oauth.signpost:signpost-core:1.2.1.2' + implementation 'com.android.support:support-v4:28.0.0' + implementation 'com.android.support:appcompat-v7:28.0.0' + implementation 'com.android.support:design:28.0.0' + implementation 'com.dropbox.core:dropbox-core-sdk:3.0.6' + // implementation 'net.sf.proguard:proguard-gradle:6.0.3' + implementation fileTree(dir: 'libs', include: ['*.jar']) + testImplementation 'junit:junit:4.12' + testImplementation 'org.mockito:mockito-core:2.15.0' +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4a7f5970..d4192066 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -28,10 +28,6 @@ You should have received a copy of the GNU General Public License along with Tod android:versionCode="56" android:versionName="2.2"> - - + android:theme="@style/TodoTxtTouchLight"> @@ -75,7 +71,8 @@ You should have received a copy of the GNU General Public License along with Tod + android:label="@string/set_preferences" + android:parentActivityName=".TodoTxtTouch"/> - + @@ -123,21 +122,6 @@ You should have received a copy of the GNU General Public License along with Tod - - - - - - - - - - diff --git a/app/src/main/java/com/todotxt/todotxttouch/AddTask.java b/app/src/main/java/com/todotxt/todotxttouch/AddTask.java index cb1650bc..c3e21561 100644 --- a/app/src/main/java/com/todotxt/todotxttouch/AddTask.java +++ b/app/src/main/java/com/todotxt/todotxttouch/AddTask.java @@ -32,20 +32,18 @@ import android.os.AsyncTask; import android.os.Bundle; import android.os.Parcelable; +import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.Gravity; -import android.view.KeyEvent; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; -import android.view.View.OnKeyListener; -import android.widget.AdapterView; import android.widget.ArrayAdapter; +import android.widget.CompoundButton; import android.widget.EditText; import android.widget.ListView; -import android.widget.TextView; -import com.actionbarsherlock.app.SherlockActivity; -import com.actionbarsherlock.view.Menu; -import com.actionbarsherlock.view.MenuItem; import com.todotxt.todotxttouch.remote.RemoteClient; import com.todotxt.todotxttouch.task.Priority; import com.todotxt.todotxttouch.task.Task; @@ -57,7 +55,7 @@ import java.util.LinkedHashSet; import java.util.Random; -public class AddTask extends SherlockActivity { +public class AddTask extends AppCompatActivity { private final static String TAG = AddTask.class.getSimpleName(); private ProgressDialog m_ProgressDialog = null; @@ -76,15 +74,14 @@ public class AddTask extends SherlockActivity { private ListView mDrawerList; + private CompoundButton.OnClickListener m_chipClickListener; + @Override public boolean onCreateOptionsMenu(Menu menu) { - getSupportMenuInflater().inflate(R.menu.add_task, menu); - - if (hasDrawer()) { - menu.findItem(R.id.menu_add_tag).setVisible(false); - } + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.add_task, menu); - return true; + return super.onCreateOptionsMenu(menu); } private void noteToSelf(Intent intent) { @@ -96,7 +93,7 @@ private void noteToSelf(Intent intent) { } @Override - public boolean onMenuItemSelected(int featureId, MenuItem item) { + public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: finish(); @@ -120,8 +117,10 @@ public boolean onMenuItemSelected(int featureId, MenuItem item) { showProjectContextMenu(); break; - } + default: + return super.onOptionsItemSelected(item); + } return true; } @@ -130,8 +129,7 @@ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.add_task); - - getSupportActionBar().setDisplayHomeAsUpEnabled(true); + android.support.v7.app.ActionBar actionBar = getSupportActionBar(); m_app = (TodoApplication) getApplication(); @@ -243,29 +241,10 @@ public void onCreate(Bundle savedInstanceState) { } } - if (hasDrawer()) { - mDrawerList = (ListView) findViewById(R.id.left_drawer); - mDrawerList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); - mLists = new ArrayList(); - mDrawerList.setAdapter(new ArrayAdapter(this, - R.layout.drawer_list_item, R.id.left_drawer_text, mLists)); - updateNavigationDrawer(); - - // Set the list's click listener - mDrawerList.setOnItemClickListener(new DrawerItemClickListener()); - textInputField.setOnKeyListener(new OnKeyListener() { - @Override - public boolean onKey(View arg0, int arg1, KeyEvent arg2) { - updateNavigationDrawer(); - - return false; - } - }); - } - textInputField.setSelection(textInputField.getText().toString() .length()); textInputField.requestFocus(); + } private boolean isAuthenticated() { @@ -457,7 +436,9 @@ protected void onSaveInstanceState(Bundle outState) { } } - /** Handle help message **/ + /** + * Handle help message + **/ public void onHelpClick() { Intent intent = new Intent(getApplicationContext(), HelpActivity.class); startActivity(intent); @@ -465,10 +446,10 @@ public void onHelpClick() { private ArrayList labelsInTaskbagAndText() { /* - * Returns the defined labels in the taskbag and the current task being - * added. This way when adding multiple tasks labels from previous lines - * will be included as well - */ + * Returns the defined labels in the taskbag and the current task being + * added. This way when adding multiple tasks labels from previous lines + * will be included as well + */ ArrayList labels = new ArrayList(); Task temp = new Task(1, textInputField.getText().toString()); @@ -492,26 +473,4 @@ private ArrayList labelsInTaskbagAndText() { // messing up sort order return new ArrayList(new LinkedHashSet(labels)); } - - /** - * Returns true if the left drawer is shown. - */ - private boolean hasDrawer() { - return findViewById(R.id.left_drawer) != null; - } - - private class DrawerItemClickListener implements - AdapterView.OnItemClickListener { - @Override - public void onItemClick(AdapterView parent, View view, int position, - long id) { - TextView tv = (TextView) view.findViewById(R.id.left_drawer_text); - String itemTitle = tv.getText().toString(); - - Log.v(TAG, "Clicked on drawer " + itemTitle); - - replaceTextAtSelection(itemTitle); - mDrawerList.clearChoices(); - } - } } diff --git a/app/src/main/java/com/todotxt/todotxttouch/LoginScreen.java b/app/src/main/java/com/todotxt/todotxttouch/LoginScreen.java index df1a5791..7e5b6b1e 100644 --- a/app/src/main/java/com/todotxt/todotxttouch/LoginScreen.java +++ b/app/src/main/java/com/todotxt/todotxttouch/LoginScreen.java @@ -24,6 +24,7 @@ */ package com.todotxt.todotxttouch; +import android.annotation.SuppressLint; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; @@ -45,6 +46,7 @@ public class LoginScreen extends Activity { private Button m_LoginButton; private BroadcastReceiver m_broadcastReceiver; + @SuppressLint("WrongViewCast") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); diff --git a/app/src/main/java/com/todotxt/todotxttouch/LogoutPreference.java b/app/src/main/java/com/todotxt/todotxttouch/LogoutPreference.java new file mode 100644 index 00000000..bbdedfcc --- /dev/null +++ b/app/src/main/java/com/todotxt/todotxttouch/LogoutPreference.java @@ -0,0 +1,88 @@ +package com.todotxt.todotxttouch; + +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.Color; +import android.support.v7.app.AlertDialog; +import android.support.v7.preference.DialogPreference; +import android.text.Spannable; +import android.text.SpannableStringBuilder; +import android.text.style.ForegroundColorSpan; +import android.util.AttributeSet; + +public class LogoutPreference extends DialogPreference { + private Context mContext; + + public LogoutPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + + mContext = context; +} + + public LogoutPreference(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); + } + + public LogoutPreference(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public LogoutPreference(Context context) { + this(context, null); + } + + + public AlertDialog displayLogoutDialog(Context context, final TodoApplication app) { + AlertDialog.Builder logoutAlert = new AlertDialog.Builder(context); + + + logoutAlert.setTitle(R.string.areyousure); + SpannableStringBuilder ss = new SpannableStringBuilder(); + + if (app.m_prefs.needToPush()) { + ss.append(context.getResources().getString(R.string.dropbox_logout_warning)); + ss.setSpan(new ForegroundColorSpan(Color.RED), 0, ss.length(), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + ss.append("\n\n"); + } + + ss.append(context.getResources().getString(R.string.dropbox_logout_explainer)); + logoutAlert.setMessage(ss); + + logoutAlert.setPositiveButton(R.string.dropbox_logout_pref_title, + new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + app + .getRemoteClientManager().getRemoteClient() + .deauthenticate(); + + // produce a logout intent and broadcast it + Intent broadcastLogoutIntent = new Intent(); + broadcastLogoutIntent + .setAction(Constants.INTENT_ACTION_LOGOUT); + app.sendBroadcast(broadcastLogoutIntent); +// finish(); + } + }); + + logoutAlert.setNegativeButton(R.string.cancel, null); + + logoutAlert.setOnCancelListener(new DialogInterface.OnCancelListener() { + @SuppressWarnings("deprecation") + @Override + public void onCancel(DialogInterface dialog) { +// removeDialog(id); + } + }); + + return logoutAlert.show(); + } + +} + +/* +AlertDialog.Builder logoutAlert = new AlertDialog.Builder(this); +// + */ diff --git a/app/src/main/java/com/todotxt/todotxttouch/Preferences.java b/app/src/main/java/com/todotxt/todotxttouch/Preferences.java index 4b8ab1a3..bc40c91a 100644 --- a/app/src/main/java/com/todotxt/todotxttouch/Preferences.java +++ b/app/src/main/java/com/todotxt/todotxttouch/Preferences.java @@ -24,180 +24,19 @@ */ package com.todotxt.todotxttouch; -import android.app.AlertDialog; -import android.app.Dialog; -import android.content.DialogInterface; -import android.content.DialogInterface.OnCancelListener; -import android.content.Intent; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager.NameNotFoundException; -import android.graphics.Color; -import android.net.Uri; import android.os.Bundle; -import android.preference.CheckBoxPreference; -import android.preference.ListPreference; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceChangeListener; -import android.preference.PreferenceActivity; -import android.preference.PreferenceScreen; -import android.text.Spannable; -import android.text.SpannableStringBuilder; -import android.text.style.ForegroundColorSpan; +import android.support.v7.app.AppCompatActivity; -public class Preferences extends PreferenceActivity { +public class Preferences extends AppCompatActivity { final static String TAG = Preferences.class.getSimpleName(); - private static final int ABOUT_DIALOG = 1; - private static final int LOGOUT_DIALOG = 2; - TodoApplication m_app; - private Preference aboutDialog; - private Preference logoutDialog; - private TodoLocationPreference mLocationPreference; - private ListPreference periodicSync; - private String version; - @SuppressWarnings("deprecation") @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - addPreferencesFromResource(R.xml.preferences); - m_app = (TodoApplication) getApplication(); - ((CheckBoxPreference) findPreference(m_app.m_prefs - .getPrependDatePrefKey())).setChecked(m_app.m_prefs - .isPrependDateEnabled()); + getSupportFragmentManager().beginTransaction() + .replace(android.R.id.content, new SettingsFragment()) + .commit(); - mLocationPreference = (TodoLocationPreference) findPreference(m_app.m_prefs - .getTodoPathKey()); - mLocationPreference.setApplication(m_app); - mLocationPreference.setDisplayWarning(m_app.m_prefs.needToPush()); - - PackageInfo packageInfo; - - try { - packageInfo = getPackageManager().getPackageInfo(getPackageName(), - 0); - Preference versionPref = (Preference) findPreference("app_version"); - versionPref.setSummary("v" + packageInfo.versionName); - version = packageInfo.versionName; - } catch (NameNotFoundException e) { - // e.printStackTrace(); - } - - aboutDialog = findPreference("app_version"); - logoutDialog = findPreference("logout_dropbox"); - periodicSync = (ListPreference) findPreference(m_app.m_prefs - .getPeriodicSyncPrefKey()); - setPeriodicSummary(periodicSync.getValue()); - periodicSync - .setOnPreferenceChangeListener(new OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, - Object newValue) { - setPeriodicSummary(newValue); - return true; - } - }); - } - - private void setPeriodicSummary(Object newValue) { - // Sync preference summary with selected entry. Ugly but this is the - // only way that works. - periodicSync.setSummary(periodicSync.getEntries()[periodicSync - .findIndexOfValue((String) newValue)]); - } - - @SuppressWarnings("deprecation") - @Override - public boolean onPreferenceTreeClick(PreferenceScreen screen, - Preference preference) { - if (preference == aboutDialog) { - showDialog(ABOUT_DIALOG); - } else if (preference == logoutDialog) { - showDialog(LOGOUT_DIALOG); - } - - return true; - } - - @Override - protected Dialog onCreateDialog(final int id) { - if (id == ABOUT_DIALOG) { - AlertDialog.Builder aboutAlert = new AlertDialog.Builder(this); - aboutAlert.setTitle("Todo.txt v" + version); - aboutAlert - .setMessage("by Gina Trapani &\nthe Todo.txt community\n\nhttp://todotxt.com"); - aboutAlert.setIcon(R.drawable.todotxt_touch_icon); - - aboutAlert.setPositiveButton("Follow us", - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface arg0, int arg1) { - Intent i = new Intent(Intent.ACTION_VIEW); - i.setData(Uri - .parse("https://mobile.twitter.com/todotxt")); - startActivity(i); - } - }); - - aboutAlert.setNegativeButton("Close", - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface arg0, int arg1) { - } - }); - - aboutAlert.setOnCancelListener(new OnCancelListener() { - @SuppressWarnings("deprecation") - @Override - public void onCancel(DialogInterface dialog) { - removeDialog(id); - } - }); - - return aboutAlert.create(); - } else if (id == LOGOUT_DIALOG) { - AlertDialog.Builder logoutAlert = new AlertDialog.Builder(this); - logoutAlert.setTitle(R.string.areyousure); - SpannableStringBuilder ss = new SpannableStringBuilder(); - - if (m_app.m_prefs.needToPush()) { - ss.append(getString(R.string.dropbox_logout_warning)); - ss.setSpan(new ForegroundColorSpan(Color.RED), 0, ss.length(), - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - ss.append("\n\n"); - } - - ss.append(getString(R.string.dropbox_logout_explainer)); - logoutAlert.setMessage(ss); - - logoutAlert.setPositiveButton(R.string.dropbox_logout_pref_title, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - ((TodoApplication) getApplication()) - .getRemoteClientManager().getRemoteClient() - .deauthenticate(); - - // produce a logout intent and broadcast it - Intent broadcastLogoutIntent = new Intent(); - broadcastLogoutIntent - .setAction(Constants.INTENT_ACTION_LOGOUT); - sendBroadcast(broadcastLogoutIntent); - finish(); - } - }); - - logoutAlert.setNegativeButton(R.string.cancel, null); - - logoutAlert.setOnCancelListener(new OnCancelListener() { - @SuppressWarnings("deprecation") - @Override - public void onCancel(DialogInterface dialog) { - removeDialog(id); - } - }); - - return logoutAlert.create(); - } - - return null; } } diff --git a/app/src/main/java/com/todotxt/todotxttouch/RelativeLayoutCheckable.java b/app/src/main/java/com/todotxt/todotxttouch/RelativeLayoutCheckable.java index befb32c3..5130e05f 100644 --- a/app/src/main/java/com/todotxt/todotxttouch/RelativeLayoutCheckable.java +++ b/app/src/main/java/com/todotxt/todotxttouch/RelativeLayoutCheckable.java @@ -26,6 +26,7 @@ import android.content.Context; import android.util.AttributeSet; +import android.util.TypedValue; import android.view.View; import android.widget.Checkable; import android.widget.RelativeLayout; @@ -33,9 +34,18 @@ public class RelativeLayoutCheckable extends RelativeLayout implements Checkable { private boolean checked; + private int m_colorChecked; + private int m_colorBackground; public RelativeLayoutCheckable(Context context, AttributeSet attrs) { super(context, attrs); + + TypedValue v = new TypedValue(); + context.getTheme().resolveAttribute(R.attr.colorAccent, v, true); + m_colorChecked = v.data; + context.getTheme().resolveAttribute(R.attr.background, v, true); + m_colorBackground = v.data; + } @Override @@ -51,15 +61,12 @@ public void setChecked(boolean checked) { if (swipeView != null) { // FIXME: this is a hack to get a grey background when swiping // without breaking highlight when selected: - this.setBackgroundColor(checked ? getResources().getColor( - R.color.activated_background) : getResources().getColor( - R.color.grey)); + this.setBackgroundColor(checked ? m_colorChecked : m_colorBackground); swipeView.setBackgroundColor(checked ? getResources().getColor( android.R.color.transparent) : getResources().getColor( R.color.white)); } else { - this.setBackgroundColor(checked ? getResources().getColor( - R.color.activated_background) : getResources().getColor( + this.setBackgroundColor(checked ? m_colorChecked : getResources().getColor( android.R.color.transparent)); } } diff --git a/app/src/main/java/com/todotxt/todotxttouch/SettingsFragment.java b/app/src/main/java/com/todotxt/todotxttouch/SettingsFragment.java new file mode 100644 index 00000000..18186666 --- /dev/null +++ b/app/src/main/java/com/todotxt/todotxttouch/SettingsFragment.java @@ -0,0 +1,169 @@ +package com.todotxt.todotxttouch; + +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.support.v7.preference.CheckBoxPreference; +import android.support.v7.preference.ListPreference; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceFragmentCompat; + +public class SettingsFragment extends PreferenceFragmentCompat { + + private static final int ABOUT_DIALOG = 1; + private static final int LOGOUT_DIALOG = 2; + TodoApplication m_app; + private Preference aboutDialog; + private Preference logoutDialog; + private ListPreference periodicSync; + private String version; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + addPreferencesFromResource(R.xml.preferences); + + m_app = (TodoApplication) getActivity().getApplication(); + ((CheckBoxPreference) findPreference(m_app.m_prefs + .getPrependDatePrefKey())).setChecked(m_app.m_prefs + .isPrependDateEnabled()); + + PackageInfo packageInfo; + + try { + packageInfo = getActivity().getPackageManager().getPackageInfo(getActivity().getPackageName(), + 0); + Preference versionPref = findPreference("app_version"); + versionPref.setSummary("v" + packageInfo.versionName); + version = packageInfo.versionName; + } catch (PackageManager.NameNotFoundException e) { + // e.printStackTrace(); + } + + aboutDialog = findPreference("app_version"); + logoutDialog = findPreference("logout_dropbox"); + periodicSync = (ListPreference) findPreference(m_app.m_prefs + .getPeriodicSyncPrefKey()); + setPeriodicSummary(periodicSync.getValue()); + periodicSync + .setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() { + @Override + public boolean onPreferenceChange(Preference preference, + Object newValue) { + setPeriodicSummary(newValue); + return true; + } + }); + } + + @Override + public void onCreatePreferences(Bundle bundle, String s) { + + } + + private void setPeriodicSummary(Object newValue) { + // Sync preference summary with selected entry. Ugly but this is the + // only way that works. + periodicSync.setSummary(periodicSync.getEntries()[periodicSync + .findIndexOfValue((String) newValue)]); + } + + @Override + public void onDisplayPreferenceDialog(Preference preference) { + + if (preference instanceof TodoLocationPreference) { + + TodoLocationPreferenceFragment dialogFragment = TodoLocationPreferenceFragment.newInstance(preference.getKey()); + dialogFragment.setApp((TodoApplication) getActivity().getApplication()); + dialogFragment.setDisplayWarning(m_app.m_prefs.needToPush()); + dialogFragment.setTargetFragment(this, 0); + dialogFragment.show(getFragmentManager(), "android.support.v7.preference" + + ".PreferenceFragment.DIALOG"); + } else if (preference instanceof LogoutPreference) { + // bit hacky but it's overkill to do the whole subclass PreferenceDialogFragmentCompat + // just for this + ((LogoutPreference) preference).displayLogoutDialog(getContext(), m_app); + } else { + super.onDisplayPreferenceDialog(preference); + } + + +// if (id == ABOUT_DIALOG) { +// AlertDialog.Builder aboutAlert = new AlertDialog.Builder(this); +// aboutAlert.setTitle("Todo.txt v" + version); +// aboutAlert +// .setMessage("by Gina Trapani &\nthe Todo.txt community\n\nhttp://todotxt.com"); +// aboutAlert.setIcon(R.drawable.todotxt_touch_icon); +// +// aboutAlert.setPositiveButton("Follow us", +// new DialogInterface.OnClickListener() { +// public void onClick(DialogInterface arg0, int arg1) { +// Intent i = new Intent(Intent.ACTION_VIEW); +// i.setData(Uri +// .parse("https://mobile.twitter.com/todotxt")); +// startActivity(i); +// } +// }); +// +// aboutAlert.setNegativeButton("Close", +// new DialogInterface.OnClickListener() { +// public void onClick(DialogInterface arg0, int arg1) { +// } +// }); +// +// aboutAlert.setOnCancelListener(new DialogInterface.OnCancelListener() { +// @SuppressWarnings("deprecation") +// @Override +// public void onCancel(DialogInterface dialog) { +// removeDialog(id); +// } +// }); +// +// return aboutAlert.create(); +// } else if (id == LOGOUT_DIALOG) { +// AlertDialog.Builder logoutAlert = new AlertDialog.Builder(this); +// logoutAlert.setTitle(R.string.areyousure); +// SpannableStringBuilder ss = new SpannableStringBuilder(); +// +// if (m_app.m_prefs.needToPush()) { +// ss.append(getString(R.string.dropbox_logout_warning)); +// ss.setSpan(new ForegroundColorSpan(Color.RED), 0, ss.length(), +// Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); +// ss.append("\n\n"); +// } +// +// ss.append(getString(R.string.dropbox_logout_explainer)); +// logoutAlert.setMessage(ss); +// +// logoutAlert.setPositiveButton(R.string.dropbox_logout_pref_title, +// new DialogInterface.OnClickListener() { +// @Override +// public void onClick(DialogInterface dialog, int which) { +// ((TodoApplication) getApplication()) +// .getRemoteClientManager().getRemoteClient() +// .deauthenticate(); +// +// // produce a logout intent and broadcast it +// Intent broadcastLogoutIntent = new Intent(); +// broadcastLogoutIntent +// .setAction(Constants.INTENT_ACTION_LOGOUT); +// sendBroadcast(broadcastLogoutIntent); +// finish(); +// } +// }); +// +// logoutAlert.setNegativeButton(R.string.cancel, null); +// +// logoutAlert.setOnCancelListener(new DialogInterface.OnCancelListener() { +// @SuppressWarnings("deprecation") +// @Override +// public void onCancel(DialogInterface dialog) { +// removeDialog(id); +// } +// }); +// +// return logoutAlert.create(); +// } + } +} diff --git a/app/src/main/java/com/todotxt/todotxttouch/TodoApplication.java b/app/src/main/java/com/todotxt/todotxttouch/TodoApplication.java index 2f82eab3..29b70e8f 100644 --- a/app/src/main/java/com/todotxt/todotxttouch/TodoApplication.java +++ b/app/src/main/java/com/todotxt/todotxttouch/TodoApplication.java @@ -27,6 +27,7 @@ import android.app.Application; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -41,6 +42,7 @@ import com.todotxt.todotxttouch.task.TaskBag; import com.todotxt.todotxttouch.task.TaskBagFactory; import com.todotxt.todotxttouch.util.Util; +import com.todotxt.todotxttouch.widget.ListWidgetProvider; import java.util.ArrayList; @@ -279,6 +281,8 @@ public void broadcastWidgetUpdate() { Log.d(TAG, "Broadcasting widget update intent"); Intent intent = new Intent(Constants.INTENT_WIDGET_UPDATE); + // https://stackoverflow.com/questions/53578252/widget-issue-broadcastqueue-background-execution-not-allowed-receiving-intent + intent.setComponent(new ComponentName(appContext, ListWidgetProvider.class)); sendBroadcast(intent); } diff --git a/app/src/main/java/com/todotxt/todotxttouch/TodoLocationPreference.java b/app/src/main/java/com/todotxt/todotxttouch/TodoLocationPreference.java index f07cf93e..20ac8b0f 100644 --- a/app/src/main/java/com/todotxt/todotxttouch/TodoLocationPreference.java +++ b/app/src/main/java/com/todotxt/todotxttouch/TodoLocationPreference.java @@ -25,119 +25,54 @@ package com.todotxt.todotxttouch; -import android.app.AlertDialog.Builder; -import android.app.Dialog; import android.content.Context; import android.content.res.TypedArray; -import android.graphics.Color; -import android.os.AsyncTask; import android.os.Parcel; import android.os.Parcelable; -import android.preference.DialogPreference; -import android.text.Spannable; -import android.text.SpannableString; -import android.text.style.ForegroundColorSpan; +import android.support.v7.preference.DialogPreference; +import android.support.v7.preference.Preference; import android.util.AttributeSet; -import android.util.Log; -import android.view.View; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.ArrayAdapter; -import android.widget.EditText; -import android.widget.ListView; -import android.widget.TextView; - -import com.todotxt.todotxttouch.remote.RemoteFolder; -import com.todotxt.todotxttouch.util.Tree; - -import java.io.File; -import java.util.List; public class TodoLocationPreference extends DialogPreference { final static String TAG = TodoLocationPreference.class.getSimpleName(); - private TodoApplication mApp; - private DisplayMode mDisplayMode = DisplayMode.NORMAL; - private boolean mDisplayWarning = false; - private ArrayAdapter mAdapter; + private String mInitialPath; - private Tree mRootFolder; - private Tree mCurrentSelection; - private ListView mListView; - private View mEmptyView; - private View mListFrame; - private EditText mEditText; - private TextView mCurrentFolderTextView; - public TodoLocationPreference(Context context, AttributeSet attrs) { - super(context, attrs); + private TodoLocationPreferenceFragment.DisplayMode mDisplayMode; + private String mCurrentSelection; - setDialogLayoutResource(R.layout.todo_location_dialog); + @Override + public CharSequence getPositiveButtonText() { + return getContext().getResources().getString(R.string.ok); } - - public boolean shouldDisplayWarning() { - return mDisplayWarning; + @Override + public CharSequence getNegativeButtonText() { + return getContext().getResources().getString(R.string.cancel); } - public void setDisplayWarning(boolean shouldDisplay) { - mDisplayWarning = shouldDisplay; + public TodoLocationPreference(Context context) { + this(context, null); } - public void setApplication(TodoApplication app) { - mApp = app; + public TodoLocationPreference(Context context, AttributeSet attrs) { + this(context, attrs, 0); } - private CharSequence getWarningMessage() { - SpannableString ss = new SpannableString(getContext().getString( - R.string.todo_path_warning)); - ss.setSpan(new ForegroundColorSpan(Color.RED), 0, ss.length(), - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - - return ss; + public TodoLocationPreference(Context context, AttributeSet attrs, int defStyleAttr) { + this(context, attrs, defStyleAttr, 0); } - @Override - protected void onClick() { - // Called when the preference is clicked - // This method displays the dialog. - // When mDisplayWarning is set, we want to display - // a warning message instead of the actual dialog - mDisplayMode = mDisplayWarning ? DisplayMode.WARNING : DisplayMode.NORMAL; - - super.onClick(); + public TodoLocationPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); } - @Override - protected void onDialogClosed(boolean positiveResult) { - // If we are displaying the warning message and the user - // clicked "I'm feeling dangerous", then redisplay the - // dialog with the default layout - if (mDisplayMode == DisplayMode.WARNING && positiveResult) { - mDisplayMode = DisplayMode.NORMAL; - showDialog(null); - - return; - } - - if (mCurrentSelection != null && positiveResult) { - String value = mCurrentSelection.getData().getPath(); - if (mDisplayMode == DisplayMode.ADD_NEW) { - value = new File(value, mEditText.getText().toString()) - .toString(); - addNew(value); - } - - if (callChangeListener(value)) { - persistString(value); - } - } - - mInitialPath = null; - } + public String getInitialPath() { return mInitialPath; } @Override protected Object onGetDefaultValue(TypedArray a, int index) { return a.getString(index); } + @SuppressWarnings("deprecation") @Override protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { if (mInitialPath == null) { @@ -147,233 +82,8 @@ protected void onSetInitialValue(boolean restoreValue, Object defaultValue) { } @Override - protected void onPrepareDialogBuilder(Builder builder) { - // Display the warning message if necessary. - // Otherwise, just use the default layout. - if (mDisplayMode == DisplayMode.WARNING) { - builder.setMessage(getWarningMessage()); - builder.setPositiveButton(R.string.todo_path_warning_override, this); - } else { - // nothing to do here... - } - } - - @Override - protected View onCreateDialogView() { - if (mDisplayMode == DisplayMode.WARNING) { - return null; - } - - return super.onCreateDialogView(); - } - - @Override - protected void onBindDialogView(View view) { - super.onBindDialogView(view); - - mCurrentFolderTextView = (TextView) view.findViewById(R.id.folder_name); - mListView = (ListView) view.findViewById(android.R.id.list); - mEmptyView = view.findViewById(android.R.id.empty); - mListFrame = view.findViewById(R.id.list_frame); - mEditText = (EditText) view.findViewById(R.id.add_new); - - if (mDisplayMode == DisplayMode.ADD_NEW) { - mEditText.setVisibility(View.VISIBLE); - mListFrame.setVisibility(View.GONE); - } else { - mEditText.setVisibility(View.GONE); - mListFrame.setVisibility(View.VISIBLE); - } - - mAdapter = new ArrayAdapter(getContext(), android.R.layout.simple_list_item_1); - mListView.setAdapter(mAdapter); - mListView.setEmptyView(mEmptyView); - - // initialize the view - initFolderTree(); - selectFolder(mCurrentSelection); - - mListView.setOnItemClickListener(new OnItemClickListener() { - public void onItemClick(AdapterView parent, View view, - int position, long id) { - - if (position == 0 && mCurrentSelection.getData().hasParent()) { - // go back up to previous directory - upToParent(); - } else if (position == mAdapter.getCount() - 1) { - // signal that AddNew was clicked - mDisplayMode = DisplayMode.ADD_NEW; - mEditText.setVisibility(View.VISIBLE); - mListFrame.setVisibility(View.GONE); - mEditText.requestFocus(); - } else { - // drill down to this directory - int index = mCurrentSelection.getData().hasParent() ? position - 1 - : position; - selectFolder(mCurrentSelection.getChild(index)); - } - } - }); - } - - private void initFolderTree() { - if (mRootFolder == null) { - mRootFolder = mCurrentSelection = new Tree(mApp - .getRemoteClientManager().getRemoteClient() - .getFolder(mInitialPath)); - } else { - // use initialPath to find the correct folder in the tree - Tree tree = findFolderInTree(mRootFolder, mInitialPath); - - if (tree != null) { - mCurrentSelection = tree; - } - } - } - - private Tree findFolderInTree(Tree tree, String path) { - if (tree.getData().getPath().equalsIgnoreCase(path)) { - return tree; - } - - if (tree.isLoaded()) { - for (Tree child : tree.getChildren()) { - Tree res = findFolderInTree(child, path); - - if (res != null) { - return res; - } - } - } - - return null; - } - - private void upToParent() { - if (mCurrentSelection.getParent() != null) { - selectFolder(mCurrentSelection.getParent()); - - return; - } - - RemoteFolder parent = mApp.getRemoteClientManager().getRemoteClient() - .getFolder(mCurrentSelection.getData().getParentPath()); - mRootFolder = new Tree(parent); - selectFolder(mRootFolder); - } - - private void addNew(String path) { - Tree tree = findFolderInTree(mCurrentSelection, path); - - if (tree == null) { - RemoteFolder folder = mApp.getRemoteClientManager() - .getRemoteClient().getFolder(path); - tree = mCurrentSelection.addChild(folder); - } - - setCurrentSelection(tree); - } - - private void setCurrentSelection(Tree folder) { - mCurrentSelection = folder; - mCurrentFolderTextView.setText(folder.getData().getName()); - populateListView(folder.getChildren()); - } - - private void selectFolder(Tree folder) { - if (!folder.isLoaded()) { - getRemoteDirectoryListing(folder); - } else { - setCurrentSelection(folder); - } - } - - private void populateListView(List> list) { - mAdapter.clear(); - - if (mCurrentSelection.getData().hasParent()) { - mAdapter.add(getContext().getString(R.string.todo_path_prev_folder, - mCurrentSelection.getData().getParentName())); - } - - if (list != null) { - for (Tree folder : list) { - mAdapter.add(folder.getData().getName()); - } - } - - mAdapter.add(getContext().getString(R.string.todo_path_add_new)); - } - - private void getRemoteDirectoryListing(final Tree folder) { - new AsyncTask>() { - @Override - protected void onPreExecute() { - showProgressIndicator(); - mAdapter.clear(); - } - - @Override - protected List doInBackground(Void... params) { - try { - return mApp.getRemoteClientManager().getRemoteClient() - .getSubFolders(folder.getData().getPath()); - } catch (Exception e) { - Log.d(TAG, "failed to get remote folder list", e); - } - - return null; - } - - @Override - protected void onPostExecute(List result) { - Dialog dialog = getDialog(); - - if (dialog == null || !dialog.isShowing()) { - return; - } - - if (result == null) { - showErrorMessage(); - - return; - } - - // if we are loading the parent of our current folder - // add the current folder as a child so we can keep it's - // children - boolean shouldAddCurrent = mCurrentSelection.getData().getParentPath() - .equals(folder.getData().getPath()); - for (RemoteFolder child : result) { - if (shouldAddCurrent && mCurrentSelection.getData().equals(child)) { - folder.addChild(mCurrentSelection); - shouldAddCurrent = false; - } else { - folder.addChild(child); - } - } - - if (shouldAddCurrent) { - // if the user created the current folder, - // it won't really exist yet, but let's not - // wipe it out. - folder.addChild(mCurrentSelection); - } - - folder.setLoaded(); - setCurrentSelection(folder); - } - }.execute(); - } - - protected void showErrorMessage() { - mEmptyView.findViewById(R.id.loading_spinner).setVisibility(View.GONE); - mEmptyView.findViewById(R.id.empty_text).setVisibility(View.VISIBLE); - } - - protected void showProgressIndicator() { - mEmptyView.findViewById(R.id.empty_text).setVisibility(View.GONE); - mEmptyView.findViewById(R.id.loading_spinner).setVisibility(View.VISIBLE); + public int getDialogLayoutResource() { + return R.layout.todo_location_dialog; } @Override @@ -384,7 +94,7 @@ protected Parcelable onSaveInstanceState() { myState.displayMode = mDisplayMode.name(); if (mCurrentSelection != null) { - myState.initialPath = mCurrentSelection.getData().getPath(); + myState.initialPath = mCurrentSelection; // FIXME: need to save the entire tree. } else { myState.initialPath = mInitialPath; @@ -403,16 +113,22 @@ protected void onRestoreInstanceState(Parcelable state) { } SavedState myState = (SavedState) state; - mDisplayMode = DisplayMode.valueOf(myState.displayMode); + mDisplayMode = TodoLocationPreferenceFragment.DisplayMode.valueOf(myState.displayMode); mInitialPath = myState.initialPath; super.onRestoreInstanceState(myState.getSuperState()); } - enum DisplayMode { - NORMAL, WARNING, ADD_NEW + public void setCurrentSelection(String mCurrentSelection) { + this.mCurrentSelection = mCurrentSelection; + persistString(mCurrentSelection); } - private static class SavedState extends BaseSavedState { + public String getCurrentSelection() { + return mCurrentSelection; + } + + + private static class SavedState extends Preference.BaseSavedState { @SuppressWarnings("unused") public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { public SavedState createFromParcel(Parcel in) { @@ -443,4 +159,4 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeString(initialPath); } } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/todotxt/todotxttouch/TodoLocationPreferenceFragment.java b/app/src/main/java/com/todotxt/todotxttouch/TodoLocationPreferenceFragment.java new file mode 100644 index 00000000..c6edb18a --- /dev/null +++ b/app/src/main/java/com/todotxt/todotxttouch/TodoLocationPreferenceFragment.java @@ -0,0 +1,375 @@ +/** + * This file is part of Todo.txt for Android, an app for managing your todo.txt file (http://todotxt.com). + *

+ * Copyright (c) 2009-2013 Todo.txt for Android contributors (http://todotxt.com) + *

+ * LICENSE: + *

+ * Todo.txt for Android 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 2 of the License, or (at your option) any + * later version. + *

+ * Todo.txt for Android 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 Todo.txt for Android. If not, see + * . + *

+ * Todo.txt for Android's source code is available at https://github.com/ginatrapani/todo.txt-android + * + * @author Todo.txt for Android contributors + * @license http://www.gnu.org/licenses/gpl.html + * @copyright 2009-2013 Todo.txt for Android contributors (http://todotxt.com) + */ +package com.todotxt.todotxttouch; + +import android.content.Context; +import android.graphics.Color; +import android.os.AsyncTask; +import android.os.Bundle; +import android.support.v7.preference.PreferenceDialogFragmentCompat; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.style.ForegroundColorSpan; +import android.util.Log; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.EditText; +import android.widget.ListView; +import android.widget.TextView; + +import com.todotxt.todotxttouch.remote.RemoteFolder; +import com.todotxt.todotxttouch.util.Tree; + +import java.io.File; +import java.util.List; + +public class TodoLocationPreferenceFragment extends PreferenceDialogFragmentCompat { + final static String TAG = TodoLocationPreferenceFragment.class.getSimpleName(); + private TodoApplication mApp; + private ArrayAdapter mAdapter; + private String mInitialPath; + private Tree mRootFolder; + private Tree mCurrentSelection; + private ListView mListView; + private View mEmptyView; + private View mListFrame; + private EditText mEditText; + private TextView mCurrentFolderTextView; + private DisplayMode mDisplayMode = DisplayMode.NORMAL; + private boolean mDisplayWarning = false; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mDisplayMode = mDisplayWarning ? DisplayMode.WARNING : DisplayMode.NORMAL; + mInitialPath = ((TodoLocationPreference)getPreference()).getInitialPath(); + } + + @Override + protected View onCreateDialogView(Context context) { + if (DisplayMode.WARNING == mDisplayMode) { + return null; + } else { + return super.onCreateDialogView(context); + } + } + + protected void onBindDialogView(View view) { + super.onBindDialogView(view); + + + mCurrentFolderTextView = (TextView) view.findViewById(R.id.folder_name); + mListView = (ListView) view.findViewById(android.R.id.list); + mEmptyView = view.findViewById(android.R.id.empty); + mListFrame = view.findViewById(R.id.list_frame); + mEditText = (EditText) view.findViewById(R.id.add_new); + + if (mDisplayMode == TodoLocationPreferenceFragment.DisplayMode.ADD_NEW) { + mEditText.setVisibility(View.VISIBLE); + mListFrame.setVisibility(View.GONE); + } else { + mEditText.setVisibility(View.GONE); + mListFrame.setVisibility(View.VISIBLE); + } + + mAdapter = new ArrayAdapter(getContext(), android.R.layout.simple_list_item_1); + mListView.setAdapter(mAdapter); + mListView.setEmptyView(mEmptyView); + + // initialize the view + initFolderTree(); + selectFolder(mCurrentSelection); + + mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + public void onItemClick(AdapterView parent, View view, + int position, long id) { + + if (position == 0 && mCurrentSelection.getData().hasParent()) { + // go back up to previous directory + upToParent(); + } else if (position == mAdapter.getCount() - 1) { + // signal that AddNew was clicked + mDisplayMode = TodoLocationPreferenceFragment.DisplayMode.ADD_NEW; + mEditText.setVisibility(View.VISIBLE); + mListFrame.setVisibility(View.GONE); + mEditText.requestFocus(); + } else { + // drill down to this directory + int index = mCurrentSelection.getData().hasParent() ? position - 1 + : position; + selectFolder(mCurrentSelection.getChild(index)); + } + } + }); + + } + +// @Override +// public void onClick(DialogInterface dialog, int which) { +// +// // I'm feeling dangerous +// if (which == -1 && mDisplayMode == DisplayMode.WARNING) { +// mDisplayMode = DisplayMode.NORMAL; +// } else { +// super.onClick(dialog, which); +// } +// } + + // @Override + public void onDialogClosed(boolean positiveResult) { + // If we are displaying the warning message and the user + // clicked "I'm feeling dangerous", then redisplay the + // dialog with the default layout + if (mDisplayMode == DisplayMode.WARNING && positiveResult) { + mDisplayWarning = false; + show(getFragmentManager(), "android.support.v7.preference" + + ".PreferenceFragment.DIALOG"); + + return; + } + + if (mCurrentSelection != null && positiveResult) { + String value = mCurrentSelection.getData().getPath(); + if (mDisplayMode == DisplayMode.ADD_NEW) { + value = new File(value, mEditText.getText().toString()) + .toString(); + addNew(value); + } + + TodoLocationPreference preference = (TodoLocationPreference) getPreference(); + + if (preference.callChangeListener(mCurrentSelection.getData().getPath())) { + preference.setCurrentSelection(mCurrentSelection.getData().getPath()); + } + } + + mInitialPath = null; + } + + @Override + protected void onPrepareDialogBuilder(android.support.v7.app.AlertDialog.Builder builder) { + // Display the warning message if necessary. + // Otherwise, just use the default layout. + if (mDisplayMode == DisplayMode.WARNING) { + builder.setMessage(getWarningMessage()); + builder.setPositiveButton(R.string.todo_path_warning_override, this); + } else { + // nothing to do here... + } + } + + private CharSequence getWarningMessage() { + SpannableString ss = new SpannableString(getContext().getString( + R.string.todo_path_warning)); + ss.setSpan(new ForegroundColorSpan(Color.RED), 0, ss.length(), + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + + return ss; + } + + + private void initFolderTree() { + if (mRootFolder == null) { + mRootFolder = mCurrentSelection = new Tree(mApp + .getRemoteClientManager().getRemoteClient() + .getFolder(mInitialPath)); + } else { + // use initialPath to find the correct folder in the tree + Tree tree = findFolderInTree(mRootFolder, mInitialPath); + + if (tree != null) { + mCurrentSelection = tree; + } + } + } + + private void upToParent() { + if (mCurrentSelection.getParent() != null) { + selectFolder(mCurrentSelection.getParent()); + + return; + } + + RemoteFolder parent = mApp.getRemoteClientManager().getRemoteClient() + .getFolder(mCurrentSelection.getData().getParentPath()); + mRootFolder = new Tree(parent); + selectFolder(mRootFolder); + } + + private void addNew(String path) { + Tree tree = findFolderInTree(mCurrentSelection, path); + + if (tree == null) { + RemoteFolder folder = mApp.getRemoteClientManager() + .getRemoteClient().getFolder(path); + tree = mCurrentSelection.addChild(folder); + } + + setCurrentSelection(tree); + } + + private void setCurrentSelection(Tree folder) { + mCurrentSelection = folder; + mCurrentFolderTextView.setText(folder.getData().getName()); + populateListView(folder.getChildren()); + } + + private Tree findFolderInTree(Tree tree, String path) { + if (tree.getData().getPath().equalsIgnoreCase(path)) { + return tree; + } + + if (tree.isLoaded()) { + for (Tree child : tree.getChildren()) { + Tree res = findFolderInTree(child, path); + + if (res != null) { + return res; + } + } + } + + return null; + } + + + private void selectFolder(Tree folder) { + if (!folder.isLoaded()) { + getRemoteDirectoryListing(folder); + } else { + setCurrentSelection(folder); + } + } + + private void populateListView(List> list) { + mAdapter.clear(); + + if (mCurrentSelection.getData().hasParent()) { + mAdapter.add(getContext().getString(R.string.todo_path_prev_folder, + mCurrentSelection.getData().getParentName())); + } + + if (list != null) { + for (Tree folder : list) { + mAdapter.add(folder.getData().getName()); + } + } + + mAdapter.add(getContext().getString(R.string.todo_path_add_new)); + } + + private void getRemoteDirectoryListing(final Tree folder) { + new AsyncTask>() { + @Override + protected void onPreExecute() { + showProgressIndicator(); + mAdapter.clear(); + } + + @Override + protected List doInBackground(Void... params) { + try { + return mApp.getRemoteClientManager().getRemoteClient() + .getSubFolders(folder.getData().getPath()); + } catch (Exception e) { + Log.d(TAG, "failed to get remote folder list", e); + } + + return null; + } + + @Override + protected void onPostExecute(List result) { +// Dialog dialog = getDialog(); + +// if (dialog == null || !dialog.isShowing()) { +// return; +// } + + if (result == null) { + showErrorMessage(); + + return; + } + + // if we are loading the parent of our current folder + // add the current folder as a child so we can keep it's + // children + boolean shouldAddCurrent = mCurrentSelection.getData().getParentPath() + .equals(folder.getData().getPath()); + for (RemoteFolder child : result) { + if (shouldAddCurrent && mCurrentSelection.getData().equals(child)) { + folder.addChild(mCurrentSelection); + shouldAddCurrent = false; + } else { + folder.addChild(child); + } + } + + if (shouldAddCurrent) { + // if the user created the current folder, + // it won't really exist yet, but let's not + // wipe it out. + folder.addChild(mCurrentSelection); + } + + folder.setLoaded(); + setCurrentSelection(folder); + } + }.execute(); + } + + protected void showErrorMessage() { + mEmptyView.findViewById(R.id.loading_spinner).setVisibility(View.GONE); + mEmptyView.findViewById(R.id.empty_text).setVisibility(View.VISIBLE); + } + + protected void showProgressIndicator() { + mEmptyView.findViewById(R.id.empty_text).setVisibility(View.GONE); + mEmptyView.findViewById(R.id.loading_spinner).setVisibility(View.VISIBLE); + } + + public static TodoLocationPreferenceFragment newInstance(String key) { + final TodoLocationPreferenceFragment fragment = new TodoLocationPreferenceFragment(); + final Bundle b = new Bundle(1); + b.putString(ARG_KEY, key); + fragment.setArguments(b); + + return fragment; + } + + public void setApp(TodoApplication mApp) { + this.mApp = mApp; + } + + public void setDisplayWarning(boolean mDisplayWarning) { + this.mDisplayWarning = mDisplayWarning; + } + + enum DisplayMode { + NORMAL, WARNING, ADD_NEW + } +} diff --git a/app/src/main/java/com/todotxt/todotxttouch/TodoPreferences.java b/app/src/main/java/com/todotxt/todotxttouch/TodoPreferences.java index 0f0a2087..2aed2beb 100644 --- a/app/src/main/java/com/todotxt/todotxttouch/TodoPreferences.java +++ b/app/src/main/java/com/todotxt/todotxttouch/TodoPreferences.java @@ -109,6 +109,10 @@ public String getAccessTokenSecret() { return m_prefs.getString(PREF_ACCESSTOKEN_SECRET, null); } + public void storeAccessToken(String accessTokenKey) { + storeAccessToken(accessTokenKey, null); + } + public void storeAccessToken(String accessTokenKey, String accessTokenSecret) { Editor editor = m_prefs.edit(); editor.putString(PREF_ACCESSTOKEN_KEY, accessTokenKey); diff --git a/app/src/main/java/com/todotxt/todotxttouch/TodoTxtTouch.java b/app/src/main/java/com/todotxt/todotxttouch/TodoTxtTouch.java index 411a7d05..7bb0b965 100644 --- a/app/src/main/java/com/todotxt/todotxttouch/TodoTxtTouch.java +++ b/app/src/main/java/com/todotxt/todotxttouch/TodoTxtTouch.java @@ -27,6 +27,7 @@ import android.annotation.TargetApi; import android.app.Activity; +import android.app.ActivityOptions; import android.app.AlertDialog; import android.app.Dialog; import android.app.ProgressDialog; @@ -49,32 +50,36 @@ import android.os.Build; import android.os.Bundle; import android.provider.CalendarContract.Events; -import android.support.v4.app.ActionBarDrawerToggle; -import android.support.v4.widget.DrawerLayout; +import android.support.design.chip.Chip; +import android.support.design.chip.ChipGroup; +import android.support.design.widget.FloatingActionButton; +import android.support.v4.widget.SwipeRefreshLayout; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.SearchView; import android.text.SpannableString; +import android.transition.Slide; +import android.transition.TransitionManager; +import android.transition.Visibility; import android.util.Log; import android.util.SparseBooleanArray; +import android.util.TypedValue; +import android.view.ActionMode; import android.view.Gravity; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; -import android.view.ViewConfiguration; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.AdapterView; import android.widget.ArrayAdapter; -import android.widget.ImageView; -import android.widget.LinearLayout; +import android.widget.CompoundButton; import android.widget.ListView; import android.widget.TextView; -import com.actionbarsherlock.app.SherlockListActivity; -import com.actionbarsherlock.view.ActionMode; -import com.actionbarsherlock.view.ActionMode.Callback; -import com.actionbarsherlock.view.Menu; -import com.actionbarsherlock.view.MenuInflater; -import com.actionbarsherlock.view.MenuItem; import com.todotxt.todotxttouch.task.FilterFactory; import com.todotxt.todotxttouch.task.Priority; import com.todotxt.todotxttouch.task.Sort; @@ -91,13 +96,11 @@ import java.util.Date; import java.util.List; -//import uk.co.senab.actionbarpulltorefresh.extras.actionbarsherlock.PullToRefreshAttacher; -//import uk.co.senab.actionbarpulltorefresh.library.DefaultHeaderTransformer; +import static android.view.View.GONE; -//import de.timroes.swipetodismiss.SwipeDismissList; -public class TodoTxtTouch extends SherlockListActivity implements - OnSharedPreferenceChangeListener, OnScrollListener { +public class TodoTxtTouch extends AppCompatActivity implements + OnSharedPreferenceChangeListener, OnScrollListener, AdapterView.OnItemClickListener { final static String TAG = TodoTxtTouch.class.getSimpleName(); @@ -107,6 +110,30 @@ public class TodoTxtTouch extends SherlockListActivity implements private static final int SYNC_CONFLICT_DIALOG = 101; private static final int ARCHIVE_DIALOG = 103; private static TodoTxtTouch currentActivityPointer = null; + private final CompoundButton.OnCheckedChangeListener m_chipChangeListener = new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton compoundButton, boolean b) { + String buttonText = compoundButton.getText().toString(); + String sigil = buttonText.substring(0, 1); + String itemText = buttonText.substring(1); + + if (sigil.equals("@")) { + if (b) { + m_app.m_contexts.add(itemText); + } else { + m_app.m_contexts.remove(itemText); + } + } else if (sigil.equals("+")) { + if (b) { + m_app.m_projects.add(itemText); + } else { + m_app.m_projects.remove(itemText); + } + } + m_app.storeFilters(); + setFilteredTasks(false); + } + }; ProgressDialog m_ProgressDialog = null; String m_DialogText = ""; Boolean m_DialogActive = false; @@ -119,14 +146,17 @@ public class TodoTxtTouch extends SherlockListActivity implements private TaskAdapter m_adapter; private BroadcastReceiver m_broadcastReceiver; private ActionMode mMode; +// private ActionMode mSearchActionMode; // Drawer variables private ArrayList m_lists; - private ListView m_drawerList; - private DrawerLayout m_drawerLayout; - private ActionBarDrawerToggle m_drawerToggle; private boolean mListScrolling = false; + private boolean m_FilterActive = false; + private SwipeRefreshLayout m_swipe; + private ChipGroup m_filterChips; + private View m_SearchSrcBar; + @Override public void onCreate(Bundle savedInstanceState) { @@ -134,6 +164,7 @@ public void onCreate(Bundle savedInstanceState) { currentActivityPointer = this; setContentView(R.layout.main); +// m_actionBar = getSupportActionBar(); m_app = (TodoApplication) getApplication(); m_app.m_prefs.registerOnSharedPreferenceChangeListener(this); @@ -141,6 +172,8 @@ public void onCreate(Bundle savedInstanceState) { m_adapter = new TaskAdapter(this, R.layout.list_item, taskBag.getTasks(), getLayoutInflater()); + m_filterChips = findViewById(R.id.filter_chips_contexts); + // listen to the ACTION_LOGOUT intent, if heard display LoginScreen // and finish() current activity IntentFilter intentFilter = new IntentFilter(); @@ -185,16 +218,15 @@ public void onReceive(Context context, Intent intent) { registerReceiver(m_broadcastReceiver, intentFilter); - setListAdapter(this.m_adapter); + // TODO +// setListAdapter(this.m_adapter); ListView lv = getListView(); lv.setTextFilterEnabled(true); lv.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); - // Setup Navigation drawer - m_drawerList = (ListView) findViewById(R.id.left_drawer); - m_drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); + lv.setAdapter(m_adapter); // Set the adapter for the list view updateNavigationDrawer(); @@ -205,14 +237,6 @@ public void onReceive(Context context, Intent intent) { lv.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { - // Don't listen to gestures on the left area of the list - // to prevent interference with the DrawerLayout - ViewConfiguration vc = ViewConfiguration.get(view.getContext()); - int deadZoneX = vc.getScaledTouchSlop(); - - if (motionEvent.getX() < deadZoneX) { - return false; - } // Only listen to item swipes if we are not scrolling the @@ -228,6 +252,27 @@ public boolean onTouch(View view, MotionEvent motionEvent) { // We must set the scrollListener after the onTouchListener, // otherwise it will not fire lv.setOnScrollListener(this); + lv.setOnItemClickListener(this); + + // swipe to refresh + m_swipe = findViewById(R.id.swipe); + m_swipe.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() { + @Override + public void onRefresh() { + // updateSyncUI will eventually be called, we'll turn off the + // SwipeLayout's spinner there + syncClient(false); + } + }); + + // FAB + FloatingActionButton fab = findViewById(R.id.fab); + fab.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + startAddTaskActivity(true); + } + }); initializeTasks(false); @@ -239,9 +284,41 @@ public boolean onTouch(View view, MotionEvent motionEvent) { Log.v(TAG, "Searched for " + m_app.m_search); m_app.storeFilters(); setFilteredTasks(false); + startActionMode(new ActionMode.Callback() { + @Override + public boolean onCreateActionMode(ActionMode actionMode, Menu menu) { + String title = m_app.getApplicationContext().getResources() + .getString(R.string.title_search_results, m_app.m_search); + actionMode.setTitle(title); + + return true; + } + + @Override + public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) { + return false; + } + + @Override + public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) { + return false; + } + + @Override + public void onDestroyActionMode(ActionMode actionMode) { + m_app.m_search = ""; + m_app.storeFilters(); + setFilteredTasks(false); + } + }); + setFilteredTasks(false); } } + private ListView getListView() { + return findViewById(R.id.list_view); + } + @Override public void onScrollStateChanged(AbsListView view, int scrollState) { // Store the scrolling state of the listview @@ -276,55 +353,7 @@ public void onScroll(AbsListView view, int firstVisibleItem, private void updateNavigationDrawer() { m_lists = contextsAndProjects(); - - if (m_lists.size() == 0) { - if (m_drawerLayout != null) { - // No contexts or projects, disable navigation drawer - m_drawerLayout.setDrawerLockMode( - DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.LEFT); - } else { - m_drawerList.setVisibility(View.GONE); - } - - getSupportActionBar().setDisplayHomeAsUpEnabled(false); - getSupportActionBar().setHomeButtonEnabled(false); - } else { - if (m_drawerLayout != null) { - m_drawerLayout.setDrawerLockMode( - DrawerLayout.LOCK_MODE_UNLOCKED, Gravity.LEFT); - m_drawerToggle = new ActionBarDrawerToggle(this, /* - * host Activity - */ - m_drawerLayout, /* DrawerLayout object */ - R.drawable.ic_drawer, /* nav drawer icon to replace 'Up' caret */ - R.string.quickfilter, /* "open drawer" description */ - R.string.app_label /* "close drawer" description */ - ) { - @Override - public void onDrawerSlide(View drawerView, float slideOffset) { - // Redraw menu to show or hide menu items - supportInvalidateOptionsMenu(); - super.onDrawerSlide(drawerView, slideOffset); - } - }; - - // Set the drawer toggle as the DrawerListener - m_drawerLayout.setDrawerListener(m_drawerToggle); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - getSupportActionBar().setHomeButtonEnabled(true); - m_drawerToggle.syncState(); - } else { - m_drawerList.setVisibility(View.VISIBLE); - } - - m_drawerList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); - m_drawerList.setAdapter(new ArrayAdapter(this, - R.layout.drawer_list_item, R.id.left_drawer_text, m_lists)); - setDrawerChoices(); - - // Set the list's click listener - m_drawerList.setOnItemClickListener(new DrawerItemClickListener()); - } + setDrawerChoices(); } private void initializeTasks(boolean force) { @@ -346,17 +375,32 @@ private void initializeTasks(boolean force) { } } - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - // If the nav drawer is open, hide action items related to the content - if (m_drawerLayout != null) { - boolean drawerOpen = m_drawerLayout.isDrawerVisible(Gravity.LEFT); - menu.findItem(R.id.add_new).setVisible(!drawerOpen); - menu.findItem(R.id.search).setVisible(!drawerOpen); - menu.findItem(R.id.sort).setVisible(!drawerOpen); - menu.findItem(R.id.share).setVisible(!drawerOpen); + @TargetApi(Build.VERSION_CODES.M) + private void setFilterState(boolean active) { + if (getResources().getConfiguration().screenWidthDp >= 720) { // force visible in large + m_FilterActive = true; + } else { + m_FilterActive = active; + + Slide slide = new Slide(); + slide.setMode(Visibility.MODE_IN | Visibility.MODE_OUT); + slide.setSlideEdge(Gravity.TOP); + slide.addTarget(m_filterChips).addTarget((ViewGroup) findViewById(R.id.swipe)); + TransitionManager.beginDelayedTransition((ViewGroup) findViewById(R.id.home_main), slide); + + } + + m_filterChips.setVisibility(m_FilterActive ? View.VISIBLE : View.GONE); + + // toggle always works regardless of size & orientation + if (!active) { + clearFilter(); + setFilteredTasks(false); } + } + @Override + public boolean onPrepareOptionsMenu(Menu menu) { menu.findItem(R.id.archive).setVisible( !m_app.m_prefs.isAutoArchiveEnabled()); @@ -401,6 +445,9 @@ protected void onResume() { // Show contextactionbar if there is a selection showContextActionBarIfNeeded(); + + setFilterState(m_FilterActive); + } @Override @@ -429,6 +476,8 @@ protected void onSaveInstanceState(Bundle outState) { outState.putBoolean("DialogActive", m_DialogActive); outState.putString("DialogText", m_DialogText); + outState.putBoolean("FilterActive", m_FilterActive); + dismissProgressDialog(false); super.onSaveInstanceState(outState); } @@ -445,38 +494,80 @@ protected void onRestoreInstanceState(Bundle state) { if (m_DialogActive) { showProgressDialog(m_DialogText); } + + setFilterState(state.getBoolean("FilterActive")); } @Override public boolean onCreateOptionsMenu(Menu menu) { - MenuInflater inflater = getSupportMenuInflater(); +// MenuInflater inflater = getSupportMenuInflater(); + MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.main, menu); this.options_menu = menu; + SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE); + SearchView searchView = (SearchView) menu.findItem(R.id.search).getActionView(); + searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); + return super.onCreateOptionsMenu(menu); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - - if (m_drawerToggle != null) { - m_drawerToggle.onConfigurationChanged(newConfig); - } } @Override public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == android.R.id.home && m_drawerLayout != null) { - if (m_drawerLayout.isDrawerOpen(m_drawerList)) { - m_drawerLayout.closeDrawer(m_drawerList); - } else { - m_drawerLayout.openDrawer(m_drawerList); - } + switch (item.getItemId()) { + case android.R.id.home: + break; + + case R.id.add_new: + startAddTaskActivity(false); + break; + + case R.id.filter: + setFilterState(!m_FilterActive); + break; + + case R.id.search: + // handled by menu item's actionview + break; + + case R.id.preferences: + startPreferencesActivity(); + break; + + case R.id.sync: + Log.v(TAG, "onMenuItemSelected: sync"); + + // same ux as swipe + m_swipe.setRefreshing(true); + syncClient(false); + + break; + + case R.id.sort: + startSortDialog(); + break; + + case R.id.share: + shareTasks(m_adapter.getItems()); + break; + + case R.id.archive: + showDialog(ARCHIVE_DIALOG); + break; + + default: + return super.onOptionsItemSelected(item); + + } - return super.onOptionsItemSelected(item); + return true; } @Override @@ -804,52 +895,16 @@ protected void onPostExecute(Boolean result) { }.execute(); } - @SuppressWarnings("deprecation") - @Override - public boolean onMenuItemSelected(int featureId, MenuItem item) { - Log.v(TAG, "onMenuItemSelected: " + item.getItemId()); - - switch (item.getItemId()) { - case R.id.add_new: - startAddTaskActivity(); - - break; - case R.id.search: - onSearchRequested(); - - break; - case R.id.preferences: - startPreferencesActivity(); - - break; - case R.id.sync: - Log.v(TAG, "onMenuItemSelected: sync"); - - syncClient(false); - - break; - case R.id.sort: - startSortDialog(); - - break; - case R.id.share: - shareTasks(m_adapter.getItems()); - - break; - case R.id.archive: - showDialog(ARCHIVE_DIALOG); + @TargetApi(23) + private void startAddTaskActivity(boolean withTransition) { + Bundle optionsBundle = null; + Intent intent = new Intent(this, AddTask.class); - break; - default: - return super.onMenuItemSelected(featureId, item); + if (withTransition) { + FloatingActionButton fab = findViewById(R.id.fab); + optionsBundle = ActivityOptions.makeScaleUpAnimation(fab, 0, 0, 0, 0).toBundle(); } - - return true; - } - - private void startAddTaskActivity() { - Intent intent = new Intent(this, AddTask.class); - startActivity(intent); + startActivity(intent, optionsBundle); } private ArrayList contextsAndProjects() { @@ -929,8 +984,7 @@ private void handleSyncConflict() { *

  • Will ask "push or pull" in manual mode. * * - * @param force - * true to force pull + * @param force true to force pull */ @SuppressWarnings("deprecation") private void syncClient(boolean force) { @@ -976,7 +1030,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { setFilteredTasks(false); } } else if (requestCode == REQUEST_PREFERENCES) { - /* Do nothing */ + /* Do nothing */ } } @@ -1129,7 +1183,9 @@ public void onCancel(DialogInterface dialog) { } } - /** Handle "add task" action. */ + /** + * Handle "add task" action. + */ public void onAddTaskClick(View v) { Intent i = new Intent(this, AddTask.class); @@ -1140,14 +1196,18 @@ public void onAddTaskClick(View v) { startActivity(i); } - /** Handle "refresh/download" action. */ + /** + * Handle "refresh/download" action. + */ public void onSyncClick(View v) { Log.v(TAG, "titlebar: sync"); syncClient(false); } - /** Handle clear filter click **/ + /** + * Handle clear filter click + **/ public void onClearClick(View v) { clearFilter(); @@ -1204,10 +1264,10 @@ void showContextActionBarIfNeeded() { } if (mMode == null) { - mMode = startActionMode(new Callback() { + mMode = startActionMode(new ActionMode.Callback() { @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { - getSupportMenuInflater().inflate(R.menu.main_long, menu); + getMenuInflater().inflate(R.menu.main_long, menu); return true; } @@ -1350,7 +1410,7 @@ void clearFilter() { m_app.m_contexts = new ArrayList(); // Collections.emptyList(); m_app.m_projects = new ArrayList(); // Collections.emptyList(); m_app.m_filters = new ArrayList(); - m_app.m_search = ""; + // m_app.m_search = ""; Filter != Search m_app.storeFilters(); setDrawerChoices(); } @@ -1378,57 +1438,23 @@ void setFilteredTasks(boolean reload) { ListView lv = getListView(); lv.setSelectionFromTop(mScrollPosition, mScrollTop); - final TextView filterText = (TextView) findViewById(R.id.filter_text); - final LinearLayout actionbar = (LinearLayout) findViewById(R.id.actionbar); - final ImageView actionbar_icon = (ImageView) findViewById(R.id.actionbar_icon); - - if (filterText != null) { - if (m_app.m_filters.size() > 0) { - String filterTitle = getString(R.string.title_filter_applied) - + " "; - int count = m_app.m_filters.size(); - - for (int i = 0; i < count; i++) { - filterTitle += m_app.m_filters.get(i) + " "; - } - - if (!Strings.isEmptyOrNull(m_app.m_search)) { - filterTitle += getString(R.string.filter_tab_search); - } - - actionbar_icon.setImageResource(R.drawable.ic_actionbar_filter); - - actionbar.setVisibility(View.VISIBLE); - filterText.setText(filterTitle); - } else if (!Strings.isEmptyOrNull(m_app.m_search)) { - if (filterText != null) { - actionbar_icon - .setImageResource(R.drawable.ic_actionbar_search); - filterText.setText(getString(R.string.title_search_results) - + " " + m_app.m_search); - - actionbar.setVisibility(View.VISIBLE); - } - } else { - filterText.setText(""); - actionbar.setVisibility(View.GONE); - } - } - +// } @Override - protected void onListItemClick(ListView l, View v, int position, long id) { - l.setItemChecked(position, l.isItemChecked(position)); + public void onItemClick(AdapterView adapterView, View view, int position, long id) { + ListView lv = (ListView) adapterView; + lv.setItemChecked(position, lv.isItemChecked(position)); showContextActionBarIfNeeded(); } private void updateSyncUI(boolean redrawList) { if (redrawList) { - // hide action bar - findViewById(R.id.actionbar).setVisibility(View.GONE); setFilteredTasks(false); } + + // cancel the SwipeLayout's refresh spinner + m_swipe.setRefreshing(false); } public void onRefreshStarted(View view) { @@ -1444,20 +1470,23 @@ public void showToast(int resid) { } private void setDrawerChoices() { - m_drawerList.clearChoices(); + + ChipGroup contextFilterChips = findViewById(R.id.filter_chips_contexts); + contextFilterChips.removeAllViews(); + boolean haveContexts = false; boolean haveProjects = false; - for (int i = 0; i < m_lists.size(); i++) { - char sigil = m_lists.get(i).charAt(0); - String item = m_lists.get(i).substring(1); + for (String item : m_lists) { - if (sigil == '@' && m_app.m_contexts.contains(item)) { - m_drawerList.setItemChecked(i, true); + Chip c = new Chip(contextFilterChips.getContext()); + c.setCheckable(true); + contextFilterChips.addView(c); + c.setText(item, TextView.BufferType.NORMAL); + c.setOnCheckedChangeListener(m_chipChangeListener); + if (m_app.m_contexts.contains(item.substring(1))) { haveContexts = true; - } else if (sigil == '+' && m_app.m_projects.contains(item)) { - m_drawerList.setItemChecked(i, true); - haveProjects = true; + c.setChecked(true); } } @@ -1480,12 +1509,6 @@ private void setDrawerChoices() { m_app.m_filters.remove(getString(R.string.filter_tab_projects)); m_app.m_projects = new ArrayList(); } - - ArrayAdapter adapter = (ArrayAdapter) m_drawerList.getAdapter(); - - if (adapter != null) { - adapter.notifyDataSetChanged(); - } } private static class ViewHolder { @@ -1495,7 +1518,7 @@ private static class ViewHolder { } public class TaskAdapter extends ArrayAdapter { - private LayoutInflater m_inflater; + private android.view.LayoutInflater m_inflater; public TaskAdapter(Context context, int textViewResourceId, List tasks, LayoutInflater inflater) { @@ -1568,30 +1591,35 @@ public View getView(int position, View convertView, ViewGroup parent) { Util.setGray(ss, task.getContexts()); holder.tasktext.setText(ss); - Resources res = getResources(); - holder.tasktext.setTextColor(res.getColor(R.color.black)); + Resources.Theme t = getTheme(); +// Resources.Theme t = parent.getContext().getTheme(); + TypedValue colorVal = new TypedValue(); + int colorAttribute; switch (task.getPriority()) { case A: - holder.taskprio.setTextColor(res.getColor(R.color.green)); + colorAttribute = R.attr.colorPriorityA; break; case B: - holder.taskprio.setTextColor(res.getColor(R.color.blue)); + colorAttribute = R.attr.colorPriorityB; break; case C: - holder.taskprio.setTextColor(res.getColor(R.color.orange)); + colorAttribute = R.attr.colorPriorityC; break; case D: - holder.taskprio.setTextColor(res.getColor(R.color.gold)); + colorAttribute = R.attr.colorPriorityD; break; default: - holder.taskprio.setTextColor(res.getColor(R.color.black)); + colorAttribute = android.R.attr.textColorPrimary; } + t.resolveAttribute(colorAttribute, colorVal, true); + holder.taskprio.setTextColor(colorVal.data); + if (task.isCompleted()) { // Log.v(TAG, "Striking through " + task.getText()); holder.tasktext.setPaintFlags(holder.tasktext @@ -1601,7 +1629,7 @@ public View getView(int position, View convertView, ViewGroup parent) { .getPaintFlags() & ~Paint.STRIKE_THRU_TEXT_FLAG); } - holder.taskage.setVisibility(View.GONE); + holder.taskage.setVisibility(GONE); if (m_app.m_prefs.isPrependDateEnabled()) { if (!task.isCompleted() @@ -1628,34 +1656,4 @@ public List getItems() { } - private class DrawerItemClickListener implements - AdapterView.OnItemClickListener { - @Override - public void onItemClick(AdapterView parent, View view, int position, - long id) { - TextView tv = (TextView) view.findViewById(R.id.left_drawer_text); - String itemTitle = tv.getText().toString(); - - Log.v(TAG, "Clicked on drawer " + itemTitle); - - if (itemTitle.substring(0, 1).equals("@") - && !m_app.m_contexts.remove(itemTitle.substring(1))) { - m_app.m_contexts = new ArrayList(); - m_app.m_contexts.add(itemTitle.substring(1)); - } else if (itemTitle.substring(0, 1).equals("+") - && !m_app.m_projects.remove(itemTitle.substring(1))) { - m_app.m_projects = new ArrayList(); - m_app.m_projects.add(itemTitle.substring(1)); - } - - setDrawerChoices(); - m_app.storeFilters(); - - if (m_drawerLayout != null) { - m_drawerLayout.closeDrawer(m_drawerList); - } - - setFilteredTasks(false); - } - } } diff --git a/app/src/main/java/com/todotxt/todotxttouch/TodoWidgetProvider.java b/app/src/main/java/com/todotxt/todotxttouch/TodoWidgetProvider.java deleted file mode 100644 index ec6cf728..00000000 --- a/app/src/main/java/com/todotxt/todotxttouch/TodoWidgetProvider.java +++ /dev/null @@ -1,182 +0,0 @@ -/** - * This file is part of Todo.txt for Android, an app for managing your todo.txt file (http://todotxt.com). - *

    - * Copyright (c) 2009-2013 Todo.txt for Android contributors (http://todotxt.com) - *

    - * LICENSE: - *

    - * Todo.txt for Android 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 2 of the License, or (at your option) any - * later version. - *

    - * Todo.txt for Android 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 Todo.txt for Android. If not, see - * . - *

    - * Todo.txt for Android's source code is available at https://github.com/ginatrapani/todo.txt-android - * - * @author Todo.txt for Android contributors - * @license http://www.gnu.org/licenses/gpl.html - * @copyright 2009-2013 Todo.txt for Android contributors (http://todotxt.com) - */ - -package com.todotxt.todotxttouch; - -import android.app.PendingIntent; -import android.appwidget.AppWidgetManager; -import android.appwidget.AppWidgetProvider; -import android.content.ComponentName; -import android.content.Context; -import android.content.ContextWrapper; -import android.content.Intent; -import android.content.res.Resources; -import android.text.SpannableString; -import android.util.Log; -import android.view.View; -import android.widget.RemoteViews; - -import com.todotxt.todotxttouch.task.Task; -import com.todotxt.todotxttouch.task.TaskBag; - -import java.util.List; - -public class TodoWidgetProvider extends AppWidgetProvider { - private static final String TAG = TodoWidgetProvider.class.getName(); - private static final int TASKS_TO_DISPLAY = 3; - - private static final int TASK_ID = 0; - private static final int TASK_PRIO = 1; - private static final int TASK_TEXT = 2; - - private final int[][] id = { - { - R.id.todoWidget_IdTask1, R.id.todoWidget_PrioTask1, - R.id.todoWidget_TextTask1 - }, - { - R.id.todoWidget_IdTask2, R.id.todoWidget_PrioTask2, - R.id.todoWidget_TextTask2 - }, - { - R.id.todoWidget_IdTask3, R.id.todoWidget_PrioTask3, - R.id.todoWidget_TextTask3 - } - }; - - @Override - public void onReceive(Context context, Intent intent) { - super.onReceive(context, intent); - - // receive intent and update widget content - if (Constants.INTENT_WIDGET_UPDATE.equals(intent.getAction())) { - Log.d(TAG, "Update widget intent received "); - - updateWidgetContent(context, AppWidgetManager.getInstance(context), null, null); - } - } - - private void updateWidgetContent(Context context, - AppWidgetManager appWidgetManager, int[] widgetIds, - RemoteViews remoteViews) { - - Log.d(TAG, "Updating TodoWidgetProvider content."); - - // get widget ID's if not provided - if (widgetIds == null) { - widgetIds = appWidgetManager.getAppWidgetIds(new ComponentName( - context, TodoWidgetProvider.class.getName())); - } - - // get remoteViews if not provided - if (remoteViews == null) { - remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget); - } - - // get taskBag from application - TaskBag taskBag = ((TodoApplication) ((ContextWrapper) context).getBaseContext()) - .getTaskBag(); - - List tasks = taskBag.getTasks(); - int taskCount = tasks.size(); - Resources resources = context.getResources(); - - for (int i = 0; i < TASKS_TO_DISPLAY; i++) { - // get task to display - if (i >= tasks.size()) { - // no more tasks to display - remoteViews.setViewVisibility(id[i][TASK_ID], View.GONE); - remoteViews.setViewVisibility(id[i][TASK_PRIO], View.GONE); - remoteViews.setViewVisibility(id[i][TASK_TEXT], View.GONE); - - continue; - } - - Task task = tasks.get(i); - - if (!task.isCompleted()) { // don't show completed tasks - // text - String taskText; - - if (task.inScreenFormat().length() > 33) { - taskText = task.inScreenFormat().substring(0, 33) + "..."; - } else { - taskText = task.inScreenFormat(); - } - - SpannableString ss = new SpannableString(taskText); - remoteViews.setTextViewText(id[i][TASK_TEXT], ss); - remoteViews.setViewVisibility(id[i][TASK_TEXT], View.VISIBLE); - - // priority - int color = R.color.white; - - switch (task.getPriority()) { - case A: - color = R.color.green; - - break; - case B: - color = R.color.blue; - - break; - case C: - color = R.color.orange; - - break; - case D: - color = R.color.gold; - default: - break; - } - - remoteViews.setTextViewText(id[i][TASK_PRIO], task.getPriority().inListFormat()); - remoteViews.setTextColor(id[i][TASK_PRIO], resources.getColor(color)); - remoteViews.setViewVisibility(id[i][TASK_PRIO], View.VISIBLE); - } - } - - remoteViews.setViewVisibility(R.id.empty, taskCount == 0 ? View.VISIBLE : View.GONE); - - appWidgetManager.updateAppWidget(widgetIds, remoteViews); - } - - @Override - public void onUpdate(Context context, AppWidgetManager appWidgetManager, - int[] appWidgetIds) { - super.onUpdate(context, appWidgetManager, appWidgetIds); - - RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget); - - Intent intent = new Intent(context, LoginScreen.class); - PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0); - remoteViews.setOnClickPendingIntent(R.id.widget_launchbutton, pendingIntent); - intent = new Intent(context, AddTask.class); - pendingIntent = PendingIntent.getActivity(context, 0, intent, 0); - remoteViews.setOnClickPendingIntent(R.id.widget_addbutton, pendingIntent); - - updateWidgetContent(context, appWidgetManager, appWidgetIds, remoteViews); - } -} diff --git a/app/src/main/java/com/todotxt/todotxttouch/remote/DropboxClientFactory.java b/app/src/main/java/com/todotxt/todotxttouch/remote/DropboxClientFactory.java new file mode 100644 index 00000000..b4da36aa --- /dev/null +++ b/app/src/main/java/com/todotxt/todotxttouch/remote/DropboxClientFactory.java @@ -0,0 +1,32 @@ +package com.todotxt.todotxttouch.remote; + +import com.dropbox.core.DbxHost; +import com.dropbox.core.DbxRequestConfig; +import com.dropbox.core.http.OkHttp3Requestor; +import com.dropbox.core.v2.DbxClientV2; + +public class DropboxClientFactory { + + private static DbxClientV2 sDbxClient; + private static String clientId = "todo.txt-android"; + + public static void init(String accessToken) { + if (sDbxClient == null) { + DbxRequestConfig requestConfig = new DbxRequestConfig(clientId); + + sDbxClient = new DbxClientV2(requestConfig, accessToken); + } + } + + public static DbxClientV2 getClient() { + if (sDbxClient == null) { + throw new IllegalStateException("Client not initialized."); + } + return sDbxClient; + } + + public static DbxClientV2 initAndGetClient(String accessToken) { + init(accessToken); + return getClient(); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/todotxt/todotxttouch/remote/DropboxFile.java b/app/src/main/java/com/todotxt/todotxttouch/remote/DropboxFile.java index c6e341da..d4819960 100644 --- a/app/src/main/java/com/todotxt/todotxttouch/remote/DropboxFile.java +++ b/app/src/main/java/com/todotxt/todotxttouch/remote/DropboxFile.java @@ -25,7 +25,7 @@ package com.todotxt.todotxttouch.remote; -import com.dropbox.client2.DropboxAPI; +import com.dropbox.core.v2.files.FileMetadata; import java.io.File; @@ -33,7 +33,7 @@ public class DropboxFile { private String remoteFile; private File localFile; private String originalRev; - private DropboxAPI.Entry loadedMetadata; + private FileMetadata loadedMetadata; private DropboxFileStatus status; private Exception error; @@ -52,14 +52,14 @@ public DropboxFile(String remoteFile, File localFile, String originalRev) { /** * @return the loadedMetadata */ - public DropboxAPI.Entry getLoadedMetadata() { + public FileMetadata getLoadedMetadata() { return loadedMetadata; } /** * @param loadedMetadata the loadedMetadata to set */ - public void setLoadedMetadata(DropboxAPI.Entry loadedMetadata) { + public void setLoadedMetadata(FileMetadata loadedMetadata) { this.loadedMetadata = loadedMetadata; } diff --git a/app/src/main/java/com/todotxt/todotxttouch/remote/DropboxFileDownloader.java b/app/src/main/java/com/todotxt/todotxttouch/remote/DropboxFileDownloader.java index 5f6eb9b3..25d3c748 100644 --- a/app/src/main/java/com/todotxt/todotxttouch/remote/DropboxFileDownloader.java +++ b/app/src/main/java/com/todotxt/todotxttouch/remote/DropboxFileDownloader.java @@ -27,9 +27,13 @@ import android.util.Log; -import com.dropbox.client2.DropboxAPI; -import com.dropbox.client2.exception.DropboxException; -import com.dropbox.client2.exception.DropboxServerException; +import com.dropbox.core.DbxDownloader; +import com.dropbox.core.DbxException; +import com.dropbox.core.v2.DbxClientV2; +import com.dropbox.core.v2.files.DeletedMetadata; +import com.dropbox.core.v2.files.FileMetadata; +import com.dropbox.core.v2.files.GetMetadataErrorException; +import com.dropbox.core.v2.files.Metadata; import com.todotxt.todotxttouch.util.Util; import java.io.File; @@ -41,16 +45,16 @@ public class DropboxFileDownloader { final static String TAG = DropboxFileDownloader.class.getSimpleName(); - private DropboxAPI dropboxApi; + private DbxClientV2 dbxClient; private DropboxFileStatus status; private Collection files; /** * @param files */ - public DropboxFileDownloader(DropboxAPI dropboxApi, + public DropboxFileDownloader(DbxClientV2 client, Collection files) { - this.dropboxApi = dropboxApi; + this.dbxClient = client; this.files = files; status = DropboxFileStatus.INITIALIZED; } @@ -92,13 +96,12 @@ public void pullFiles() { private void loadMetadata(DropboxFile file) { Log.d(TAG, "Loading metadata for " + file.getRemoteFile()); - DropboxAPI.Entry metadata = null; + Metadata metadata = null; try { - metadata = dropboxApi.metadata(file.getRemoteFile(), 0, null, - false, null); - } catch (DropboxServerException se) { - if (se.error == DropboxServerException._404_NOT_FOUND) { + metadata = dbxClient.files().getMetadata(file.getRemoteFile()); + } catch (GetMetadataErrorException mde) { + if (mde.errorValue.isPath() && mde.errorValue.getPathValue().isNotFound()) { Log.d(TAG, "metadata NOT found! Returning NOT_FOUND status."); file.setStatus(DropboxFileStatus.NOT_FOUND); @@ -106,22 +109,31 @@ private void loadMetadata(DropboxFile file) { return; } - throw new RemoteException("Server Exception: " + se.error + " " + se.reason, se); - } catch (DropboxException e) { + throw new RemoteException("Server Exception: " + mde.errorValue + " " + mde.getUserMessage(), mde); + } catch (DbxException e) { throw new RemoteException("Dropbox Exception: " + e.getMessage(), e); } - Log.d(TAG, "Metadata retrieved. rev on Dropbox = " + metadata.rev); - Log.d(TAG, "local rev = " + file.getOriginalRev()); + FileMetadata fileMetadata = null; + if(metadata instanceof FileMetadata) { + file.setLoadedMetadata((FileMetadata) metadata); + fileMetadata = (FileMetadata) metadata; + Log.d(TAG, "Metadata retrieved. rev on Dropbox = " + fileMetadata.getRev()); + } - file.setLoadedMetadata(metadata); + DeletedMetadata deletedMetadata = null; + if(metadata instanceof DeletedMetadata){ + deletedMetadata = (DeletedMetadata) metadata; + } + + Log.d(TAG, "local rev = " + file.getOriginalRev()); - if (metadata.rev.equals(file.getOriginalRev())) { + if (fileMetadata != null && fileMetadata.getRev().equals(file.getOriginalRev())) { // don't bother downloading if the rev is the same Log.d(TAG, "revs match. returning NOT_CHANGED status."); file.setStatus(DropboxFileStatus.NOT_CHANGED); - } else if (metadata.isDeleted) { + } else if (deletedMetadata != null) { Log.d(TAG, "File marked as deleted on Dropbox! Returning NOT_FOUND status."); file.setStatus(DropboxFileStatus.NOT_FOUND); } else { @@ -134,7 +146,7 @@ private void loadMetadata(DropboxFile file) { private void loadFile(DropboxFile file) { Log.d(TAG, "Downloading " + file.getRemoteFile() + " at rev = " - + file.getLoadedMetadata().rev); + + file.getLoadedMetadata().getRev()); File localFile = file.getLocalFile(); @@ -156,11 +168,12 @@ private void loadFile(DropboxFile file) { } try { - dropboxApi.getFile(file.getRemoteFile(), file.getLoadedMetadata().rev, outputStream, - null); + DbxDownloader downloader = dbxClient.files().download(file.getRemoteFile(), + file.getLoadedMetadata().getRev()); + downloader.download(outputStream); outputStream.flush(); outputStream.close(); - } catch (DropboxException e) { + } catch (DbxException e) { throw new RemoteException("Cannot get file from Dropbox", e); } catch (IOException e) { throw new RemoteException("Failed to find file", e); diff --git a/app/src/main/java/com/todotxt/todotxttouch/remote/DropboxFileUploader.java b/app/src/main/java/com/todotxt/todotxttouch/remote/DropboxFileUploader.java index 8e269eb0..5f8308c4 100644 --- a/app/src/main/java/com/todotxt/todotxttouch/remote/DropboxFileUploader.java +++ b/app/src/main/java/com/todotxt/todotxttouch/remote/DropboxFileUploader.java @@ -27,10 +27,14 @@ import android.util.Log; -import com.dropbox.client2.DropboxAPI; -import com.dropbox.client2.exception.DropboxException; -import com.dropbox.client2.exception.DropboxServerException; -import com.dropbox.client2.exception.DropboxUnlinkedException; +import com.dropbox.core.DbxException; +import com.dropbox.core.v2.DbxClientV2; +import com.dropbox.core.v2.files.DeletedMetadata; +import com.dropbox.core.v2.files.FileMetadata; +import com.dropbox.core.v2.files.GetMetadataErrorException; +import com.dropbox.core.v2.files.Metadata; +import com.dropbox.core.v2.files.UploadUploader; +import com.dropbox.core.v2.files.WriteMode; import com.todotxt.todotxttouch.util.Util; import java.io.File; @@ -38,11 +42,12 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.util.Collection; +import java.util.Date; public class DropboxFileUploader { final static String TAG = DropboxFileUploader.class.getSimpleName(); - private DropboxAPI dropboxApi; + private DbxClientV2 dbxClient; private DropboxFileStatus status; private Collection files; private boolean overwrite; @@ -50,9 +55,9 @@ public class DropboxFileUploader { /** * @param files */ - public DropboxFileUploader(DropboxAPI dropboxApi, + public DropboxFileUploader(DbxClientV2 client, Collection files, boolean overwrite) { - this.dropboxApi = dropboxApi; + this.dbxClient = client; this.files = files; this.overwrite = overwrite; status = DropboxFileStatus.INITIALIZED; @@ -96,35 +101,45 @@ public void pushFiles() { private void loadMetadata(DropboxFile file) { Log.d(TAG, "Loading metadata for " + file.getRemoteFile()); - DropboxAPI.Entry metadata = null; + Metadata metadata = null; try { - metadata = dropboxApi.metadata(file.getRemoteFile(), 1, null, - false, null); - } catch (DropboxServerException se) { - if (se.error == DropboxServerException._404_NOT_FOUND) { + metadata = metadata = dbxClient.files().getMetadata(file.getRemoteFile()); + } catch (GetMetadataErrorException mde) { + if (mde.errorValue.isPath() && mde.errorValue.getPathValue().isNotFound()) { Log.d(TAG, "metadata NOT found! Returning NOT_FOUND status."); file.setStatus(DropboxFileStatus.NOT_FOUND); return; } - throw new RemoteException("Server Exception: " + se.error + " " + se.reason, se); - } catch (DropboxException e) { + throw new RemoteException("Server Exception: " + mde.errorValue + " " + mde.getUserMessage(), mde); + } catch (DbxException e) { throw new RemoteException("Dropbox Exception: " + e.getMessage(), e); } - Log.d(TAG, "Metadata retrieved. rev on Dropbox = " + metadata.rev); + DeletedMetadata deletedMetadata = null; + if(metadata instanceof DeletedMetadata){ + deletedMetadata = (DeletedMetadata) metadata; + } + + FileMetadata fileMetadata = null; + if(metadata instanceof FileMetadata) { + file.setLoadedMetadata((FileMetadata) metadata); + fileMetadata = (FileMetadata) metadata; + Log.d(TAG, "Metadata retrieved. rev on Dropbox = " + fileMetadata.getRev()); + } + Log.d(TAG, "local rev = " + file.getOriginalRev()); - if (metadata.isDeleted) { + if (deletedMetadata != null) { Log.d(TAG, "File marked as deleted on Dropbox! Returning NOT_FOUND status."); file.setStatus(DropboxFileStatus.NOT_FOUND); } else { - file.setLoadedMetadata(metadata); + file.setLoadedMetadata(fileMetadata); - if (!overwrite && !metadata.rev.equals(file.getOriginalRev())) { + if (!overwrite && !fileMetadata.getRev().equals(file.getOriginalRev())) { Log.d(TAG, "revs don't match! Returning CONFLICT status."); file.setStatus(DropboxFileStatus.CONFLICT); @@ -157,29 +172,22 @@ private void uploadFile(DropboxFile file) { String rev = null; if (file.getLoadedMetadata() != null) { - rev = file.getLoadedMetadata().rev; + rev = file.getLoadedMetadata().getRev(); } Log.d(TAG, "Sending parent_rev = " + rev); - FileInputStream inputStream; - - try { - inputStream = new FileInputStream(localFile); - } catch (FileNotFoundException e1) { + FileMetadata metadata = null; + try (FileInputStream inputStream = new FileInputStream(localFile)) { + metadata = dbxClient.files().uploadBuilder(file.getRemoteFile()) + .withMode(WriteMode.OVERWRITE) + .withClientModified(new Date(localFile.lastModified())) + .uploadAndFinish(inputStream); + } + catch (FileNotFoundException e1) { throw new RemoteException("File " + localFile.getAbsolutePath() + " not found", e1); - } - - DropboxAPI.Entry metadata = null; - - try { - metadata = dropboxApi.putFile(file.getRemoteFile(), inputStream, localFile.length(), - rev, null); - inputStream.close(); - } catch (DropboxUnlinkedException e) { - throw new RemoteException("User has unlinked.", e); - } catch (DropboxException e) { + } catch (DbxException e) { e.printStackTrace(); throw new RemoteException("Something went wrong while uploading: " + e.getMessage(), e); @@ -189,11 +197,11 @@ private void uploadFile(DropboxFile file) { throw new RemoteException("Problem with IO", e); } - Log.d(TAG, "Upload succeeded. new rev = " + metadata.rev + ". path = " + metadata.path); + Log.d(TAG, "Upload succeeded. new rev = " + metadata.getRev() + ". path = " + metadata.getPathDisplay()); file.setLoadedMetadata(metadata); - if (!metadata.path.equalsIgnoreCase(file.getRemoteFile())) { + if (!metadata.getPathDisplay().equalsIgnoreCase(file.getRemoteFile())) { // If the uploaded remote path does not match our expected // remotePath, // then a conflict occurred and we should announce the conflict to diff --git a/app/src/main/java/com/todotxt/todotxttouch/remote/DropboxRemoteClient.java b/app/src/main/java/com/todotxt/todotxttouch/remote/DropboxRemoteClient.java index af09c5cf..64d4d884 100644 --- a/app/src/main/java/com/todotxt/todotxttouch/remote/DropboxRemoteClient.java +++ b/app/src/main/java/com/todotxt/todotxttouch/remote/DropboxRemoteClient.java @@ -25,17 +25,18 @@ package com.todotxt.todotxttouch.remote; +import android.content.Context; import android.content.Intent; import android.util.Log; -import com.dropbox.client2.DropboxAPI; -import com.dropbox.client2.DropboxAPI.Entry; -import com.dropbox.client2.android.AndroidAuthSession; -import com.dropbox.client2.exception.DropboxException; -import com.dropbox.client2.exception.DropboxServerException; -import com.dropbox.client2.session.AccessTokenPair; -import com.dropbox.client2.session.AppKeyPair; -import com.dropbox.client2.session.Session.AccessType; +import com.dropbox.core.DbxException; +import com.dropbox.core.android.Auth; + +import com.dropbox.core.v2.DbxClientV2; +import com.dropbox.core.v2.files.FolderMetadata; +import com.dropbox.core.v2.files.ListFolderErrorException; +import com.dropbox.core.v2.files.ListFolderResult; +import com.dropbox.core.v2.files.Metadata; import com.todotxt.todotxttouch.Constants; import com.todotxt.todotxttouch.R; import com.todotxt.todotxttouch.TodoApplication; @@ -51,7 +52,6 @@ class DropboxRemoteClient implements RemoteClient { private static final String TODO_TXT_REMOTE_FILE_NAME = "todo.txt"; private static final String DONE_TXT_REMOTE_FILE_NAME = "done.txt"; - private static final AccessType ACCESS_TYPE = AccessType.DROPBOX; private static final File TODO_TXT_TMP_FILE = new File( TodoApplication.getAppContetxt().getFilesDir(), "tmp/todo.txt"); @@ -59,7 +59,7 @@ class DropboxRemoteClient implements RemoteClient { TodoApplication.getAppContetxt().getFilesDir(), "tmp/done.txt"); - private DropboxAPI dropboxApi; + private DbxClientV2 client; private TodoApplication todoApplication; private TodoPreferences sharedPreferences; @@ -74,25 +74,6 @@ public Client getClient() { return Client.DROPBOX; } - /** - * Get the stored key - secret pair for authenticating the user - * - * @return a string array with key and secret - */ - private AccessTokenPair getStoredKeys() { - String key = null; - String secret = null; - - key = sharedPreferences.getAccessToken(); - secret = sharedPreferences.getAccessTokenSecret(); - - if (key != null && secret != null) { - return new AccessTokenPair(key, secret); - } - - return null; - } - /** * Store the key - secret pair for an authenticated user. * @@ -113,41 +94,42 @@ private void clearAuthToken() { @Override public boolean authenticate() { - String consumerKey = todoApplication.getResources().getText(R.string.dropbox_consumer_key) - .toString(); - String consumerSecret = todoApplication.getText(R.string.dropbox_consumer_secret) - .toString(); - consumerKey = consumerKey.replaceFirst("^db-", ""); - - AppKeyPair appKeys = new AppKeyPair(consumerKey, consumerSecret); - AndroidAuthSession session = new AndroidAuthSession(appKeys, ACCESS_TYPE); - dropboxApi = new DropboxAPI(session); - - AccessTokenPair access = getStoredKeys(); - - if (access != null) { - dropboxApi.getSession().setAccessTokenPair(access); + String accessToken = sharedPreferences.getAccessToken(); + if (accessToken == null){ + Log.i(TAG, "Stored accessToken is null. Retrieving new token."); + accessToken = Auth.getOAuth2Token(); + if(accessToken != null) { + Log.i(TAG, "Successfully retrieved new token."); + sharedPreferences.storeAccessToken(accessToken); + this.client = DropboxClientFactory.initAndGetClient(accessToken); + Log.i(TAG, "Successfully initialized new Dropbox client."); + Log.i(TAG, "Authentication completed successfully."); + return true; + } + } else { + this.client = DropboxClientFactory.initAndGetClient(accessToken); + Log.i(TAG, "Successfully initialized new Dropbox client."); + Log.i(TAG, "Authentication completed successfully."); + return true; } - - return true; + return false; } @Override public void deauthenticate() { clearAuthToken(); - dropboxApi.getSession().unlink(); TODO_TXT_TMP_FILE.delete(); DONE_TXT_TMP_FILE.delete(); } @Override public boolean isAuthenticated() { - return dropboxApi.getSession().isLinked(); + return hasToken(); } @Override public boolean isLoggedIn() { - return dropboxApi.getSession().isLinked(); + return hasToken(); } @Override @@ -176,7 +158,7 @@ public PullTodoResult pullTodo() { dropboxFiles.add(doneFile); DropboxFileDownloader downloader = new DropboxFileDownloader( - dropboxApi, dropboxFiles); + client, dropboxFiles); downloader.pullFiles(); File downloadedTodoFile = null; @@ -185,13 +167,13 @@ public PullTodoResult pullTodo() { if (todoFile.getStatus() == DropboxFileStatus.SUCCESS) { downloadedTodoFile = todoFile.getLocalFile(); sharedPreferences.storeFileRevision(TodoPreferences.PREF_TODO_REV, - todoFile.getLoadedMetadata().rev); + todoFile.getLoadedMetadata().getRev()); } if (doneFile.getStatus() == DropboxFileStatus.SUCCESS) { downloadedDoneFile = doneFile.getLocalFile(); sharedPreferences.storeFileRevision(TodoPreferences.PREF_DONE_REV, - doneFile.getLoadedMetadata().rev); + doneFile.getLoadedMetadata().getRev()); } return new PullTodoResult(downloadedTodoFile, downloadedDoneFile); @@ -214,7 +196,7 @@ public void pushTodo(File todoFile, File doneFile, boolean overwrite) { .getFileRevision(TodoPreferences.PREF_DONE_REV))); } - DropboxFileUploader uploader = new DropboxFileUploader(dropboxApi, + DropboxFileUploader uploader = new DropboxFileUploader(client, dropboxFiles, overwrite); uploader.pushFiles(); @@ -224,7 +206,7 @@ public void pushTodo(File todoFile, File doneFile, boolean overwrite) { if (todoDropboxFile.getStatus() == DropboxFileStatus.SUCCESS) { sharedPreferences.storeFileRevision( TodoPreferences.PREF_TODO_REV, - todoDropboxFile.getLoadedMetadata().rev); + todoDropboxFile.getLoadedMetadata().getRev()); } } if (dropboxFiles.size() > 1) { @@ -232,7 +214,7 @@ public void pushTodo(File todoFile, File doneFile, boolean overwrite) { if (doneDropboxFile.getStatus() == DropboxFileStatus.SUCCESS) { sharedPreferences.storeFileRevision( TodoPreferences.PREF_DONE_REV, - doneDropboxFile.getLoadedMetadata().rev); + doneDropboxFile.getLoadedMetadata().getRev()); } } } @@ -240,32 +222,23 @@ public void pushTodo(File todoFile, File doneFile, boolean overwrite) { @Override public boolean startLogin() { - dropboxApi.getSession().startAuthentication( - todoApplication.getApplicationContext()); + Context cxt = todoApplication.getApplicationContext(); + String appKey = cxt.getString(R.string.dropbox_consumer_key).toString() + .replaceFirst("^db-", ""); + Auth.startOAuth2Authentication( + cxt, appKey); return true; } @Override public boolean finishLogin() { - if (dropboxApi.getSession().authenticationSuccessful()) { + String accessToken = Auth.getOAuth2Token(); + if (accessToken != null) { Log.i(TAG, "Dropbox authentication successful."); - - try { - dropboxApi.getSession().finishAuthentication(); - - Log.i(TAG, "Dropbox authentication complete."); - - AccessTokenPair tokens = dropboxApi.getSession() - .getAccessTokenPair(); - - storeKeys(tokens.key, tokens.secret); - } catch (IllegalStateException e) { - Log.i("DbAuthLog", "Error authenticating", e); - - return false; - } - + sharedPreferences.storeAccessToken(accessToken); + Log.i(TAG, "Dropbox authentication complete."); + this.client = DropboxClientFactory.initAndGetClient(accessToken); return true; } @@ -274,6 +247,11 @@ public boolean finishLogin() { return false; } + boolean hasToken(){ + return sharedPreferences.getAccessToken() != null + && sharedPreferences.getAccessToken() != ""; + } + void sendBroadcast(Intent intent) { todoApplication.sendBroadcast(intent); } @@ -282,10 +260,6 @@ void showToast(String string) { Util.showToastLong(todoApplication, string); } - DropboxAPI getAPI() { - return dropboxApi; - } - String getRemotePath() { return sharedPreferences.getTodoFilePath(); } @@ -308,29 +282,33 @@ public List getSubFolders(String path) { List results = new ArrayList(); try { - Log.d(TAG, "getting file listiing for path " + path); + Log.d(TAG, "getting file listing for path " + path); + if (path.equals("/")) { + Log.d(TAG, "setting path to '' from '/' to avoid bad request."); + path = ""; + } - Entry metadata = dropboxApi.metadata(path, 0, null, true, null); + ListFolderResult folders = client.files().listFolder(path); - Log.d(TAG, "num entries returned: " + metadata.contents.size()); + Log.d(TAG, "num entries returned: " + folders.getEntries().size()); - for (Entry e : metadata.contents) { - if (e.isDir && !e.isDeleted) { - results.add(new DropboxRemoteFolder(e)); + for (Metadata m : folders.getEntries()) { + if (m instanceof FolderMetadata) { + results.add(new DropboxRemoteFolder((FolderMetadata) m)); } } - } catch (DropboxServerException dse) { - if (dse.error != DropboxServerException._404_NOT_FOUND) { + } catch (ListFolderErrorException lfe) { + if (lfe.errorValue.toString() != "NOT_FOUND") { Log.e(TAG, "Error getting folders for path: " + path); - Log.e(TAG, "Dropbox returned error code: " + dse.error); + Log.e(TAG, "Dropbox returned error code: " + lfe.errorValue); - throw new RemoteException("Failed to get folder listing from Dropbox", dse); + throw new RemoteException("Failed to get folder listing from Dropbox", lfe); } // A 404 is OK. We will create the directory if necessary when we // push Log.i(TAG, "Remote path not found: " + path); - } catch (DropboxException e) { + } catch (DbxException e) { Log.e(TAG, "Error getting folders for path: " + path); throw new RemoteException("Failed to get folder listing from Dropbox", e); diff --git a/app/src/main/java/com/todotxt/todotxttouch/remote/DropboxRemoteFolder.java b/app/src/main/java/com/todotxt/todotxttouch/remote/DropboxRemoteFolder.java index 6def3ba4..7cc8f6cf 100644 --- a/app/src/main/java/com/todotxt/todotxttouch/remote/DropboxRemoteFolder.java +++ b/app/src/main/java/com/todotxt/todotxttouch/remote/DropboxRemoteFolder.java @@ -25,23 +25,21 @@ package com.todotxt.todotxttouch.remote; -import com.dropbox.client2.DropboxAPI; -import com.todotxt.todotxttouch.util.Path; +import com.dropbox.core.v2.files.FolderMetadata; import com.todotxt.todotxttouch.util.Strings; class DropboxRemoteFolder extends RemoteFolderImpl { private static final String ROOT_PATH = "/"; private static final String ROOT_NAME = "Dropbox"; - public DropboxRemoteFolder(DropboxAPI.Entry metadata) { - super(metadata.path, metadata.fileName(), metadata.parentPath(), Path - .fileName(metadata.parentPath())); - } - public DropboxRemoteFolder(String path) { super(Strings.isBlank(path) ? ROOT_PATH : path); } + public DropboxRemoteFolder(FolderMetadata metadata){ + super(metadata); + } + @Override public String getName() { if (mPath.equals(ROOT_PATH)) { diff --git a/app/src/main/java/com/todotxt/todotxttouch/remote/RemoteFolderImpl.java b/app/src/main/java/com/todotxt/todotxttouch/remote/RemoteFolderImpl.java index 4922a3d6..0dcca752 100644 --- a/app/src/main/java/com/todotxt/todotxttouch/remote/RemoteFolderImpl.java +++ b/app/src/main/java/com/todotxt/todotxttouch/remote/RemoteFolderImpl.java @@ -25,6 +25,7 @@ package com.todotxt.todotxttouch.remote; +import com.dropbox.core.v2.files.FolderMetadata; import com.todotxt.todotxttouch.util.Path; import com.todotxt.todotxttouch.util.Strings; @@ -48,6 +49,13 @@ public RemoteFolderImpl(String path, String name, String parentPath, String pare mParentName = parentName; } + public RemoteFolderImpl(FolderMetadata folderMetadata){ + mPath = folderMetadata.getPathDisplay(); + mName = folderMetadata.getName(); + mParentPath = Path.parentPath(mPath); + mParentName = Path.fileName(mParentPath); + } + @Override public String getName() { return mName; diff --git a/app/src/main/java/com/todotxt/todotxttouch/widget/ListWidgetService.java b/app/src/main/java/com/todotxt/todotxttouch/widget/ListWidgetService.java index fe8a26b6..204844ff 100644 --- a/app/src/main/java/com/todotxt/todotxttouch/widget/ListWidgetService.java +++ b/app/src/main/java/com/todotxt/todotxttouch/widget/ListWidgetService.java @@ -28,7 +28,6 @@ import android.annotation.TargetApi; import android.content.Context; import android.content.Intent; -import android.content.res.Resources; import android.os.Build; import android.text.SpannableString; import android.text.Spanned; @@ -42,6 +41,7 @@ import com.todotxt.todotxttouch.R; import com.todotxt.todotxttouch.TodoApplication; import com.todotxt.todotxttouch.task.FilterFactory; +import com.todotxt.todotxttouch.task.Priority; import com.todotxt.todotxttouch.task.Task; import com.todotxt.todotxttouch.task.TaskBag; import com.todotxt.todotxttouch.util.Strings; @@ -50,6 +50,11 @@ import java.util.ArrayList; import java.util.List; +import static com.todotxt.todotxttouch.task.Priority.A; +import static com.todotxt.todotxttouch.task.Priority.B; +import static com.todotxt.todotxttouch.task.Priority.C; +import static com.todotxt.todotxttouch.task.Priority.D; + @TargetApi(Build.VERSION_CODES.HONEYCOMB) public class ListWidgetService extends RemoteViewsService { @Override @@ -98,6 +103,7 @@ public RemoteViews getLoadingView() { @Override public RemoteViews getViewAt(int position) { Task task = tasks.get(position); + int priorityViewId = R.id.listwidget_taskprio_other; RemoteViews rv = new RemoteViews(m_app.getPackageName(), R.layout.listwidget_item); SpannableString ss = new SpannableString(task.inScreenFormat()); @@ -109,33 +115,25 @@ public RemoteViews getViewAt(int position) { } rv.setTextViewText(R.id.listwidget_tasktext, ss); - - rv.setTextViewText(R.id.listwidget_taskprio, task.getPriority().inListFormat()); - int color = R.color.black; - - switch (task.getPriority()) { - case A: - color = R.color.green; - - break; - case B: - color = R.color.blue; - - break; - case C: - color = R.color.orange; - - break; - case D: - color = R.color.gold; - - break; - default: - color = R.color.black; - } - - Resources resources = m_app.getResources(); - rv.setTextColor(R.id.listwidget_taskprio, resources.getColor(color)); + final Priority priority = task.getPriority(); + rv.setTextViewText(priorityViewId, priority.inListFormat()); + + // can't seem to resolve theme attributes inside the service, so resorting + // to including all views in the layout and controlling visibility + rv.setViewVisibility(R.id.listwidget_taskprio_a, priority == A ? View.VISIBLE : View.INVISIBLE); + rv.setViewVisibility(R.id.listwidget_taskprio_b, priority == B ? View.VISIBLE : View.INVISIBLE); + rv.setViewVisibility(R.id.listwidget_taskprio_c, priority == C ? View.VISIBLE : View.INVISIBLE); + rv.setViewVisibility(R.id.listwidget_taskprio_d, priority == D ? View.VISIBLE : View.INVISIBLE); + rv.setViewVisibility(R.id.listwidget_taskprio_other, + (priority == A || priority == B || priority == C || priority == D) + ? View.INVISIBLE : View.VISIBLE); + + // bit clumsy but avoids defining the task priority text strings in xml and in the Priority class + rv.setTextViewText(R.id.listwidget_taskprio_a, task.getPriority().inListFormat()); + rv.setTextViewText(R.id.listwidget_taskprio_b, task.getPriority().inListFormat()); + rv.setTextViewText(R.id.listwidget_taskprio_c, task.getPriority().inListFormat()); + rv.setTextViewText(R.id.listwidget_taskprio_d, task.getPriority().inListFormat()); + rv.setTextViewText(R.id.listwidget_taskprio_other, task.getPriority().inListFormat()); if (m_app.m_prefs.isPrependDateEnabled() && !task.isCompleted() diff --git a/app/src/main/res/drawable-hdpi/todotxt_touch_icon.png b/app/src/main/res/drawable-hdpi/todotxt_touch_icon.png index e7e54be0..889f78b0 100644 Binary files a/app/src/main/res/drawable-hdpi/todotxt_touch_icon.png and b/app/src/main/res/drawable-hdpi/todotxt_touch_icon.png differ diff --git a/app/src/main/res/drawable-ldpi/todotxt_touch_icon.png b/app/src/main/res/drawable-ldpi/todotxt_touch_icon.png index 203d1fff..66f083f8 100644 Binary files a/app/src/main/res/drawable-ldpi/todotxt_touch_icon.png and b/app/src/main/res/drawable-ldpi/todotxt_touch_icon.png differ diff --git a/app/src/main/res/drawable-mdpi/todotxt_touch_icon.png b/app/src/main/res/drawable-mdpi/todotxt_touch_icon.png index 28a4ff31..ac80234a 100644 Binary files a/app/src/main/res/drawable-mdpi/todotxt_touch_icon.png and b/app/src/main/res/drawable-mdpi/todotxt_touch_icon.png differ diff --git a/app/src/main/res/drawable-xhdpi/todotxt_touch_icon.png b/app/src/main/res/drawable-xhdpi/todotxt_touch_icon.png index 7babbb62..6bab2bbc 100644 Binary files a/app/src/main/res/drawable-xhdpi/todotxt_touch_icon.png and b/app/src/main/res/drawable-xhdpi/todotxt_touch_icon.png differ diff --git a/app/src/main/res/drawable-xxhdpi/todotxt_touch_icon.png b/app/src/main/res/drawable-xxhdpi/todotxt_touch_icon.png index e314601a..d6e8ddbc 100644 Binary files a/app/src/main/res/drawable-xxhdpi/todotxt_touch_icon.png and b/app/src/main/res/drawable-xxhdpi/todotxt_touch_icon.png differ diff --git a/app/src/main/res/drawable/action_accept_tintable.xml b/app/src/main/res/drawable/action_accept_tintable.xml new file mode 100644 index 00000000..4f67423f --- /dev/null +++ b/app/src/main/res/drawable/action_accept_tintable.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/app/src/main/res/drawable/action_add_tintable.xml b/app/src/main/res/drawable/action_add_tintable.xml new file mode 100644 index 00000000..535c63be --- /dev/null +++ b/app/src/main/res/drawable/action_add_tintable.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/app/src/main/res/drawable/action_complete_tintable.xml b/app/src/main/res/drawable/action_complete_tintable.xml new file mode 100644 index 00000000..802fab27 --- /dev/null +++ b/app/src/main/res/drawable/action_complete_tintable.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/app/src/main/res/drawable/action_delete_tintable.xml b/app/src/main/res/drawable/action_delete_tintable.xml new file mode 100644 index 00000000..e323eed1 --- /dev/null +++ b/app/src/main/res/drawable/action_delete_tintable.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/app/src/main/res/drawable/action_edit_tintable.xml b/app/src/main/res/drawable/action_edit_tintable.xml new file mode 100644 index 00000000..b6a71432 --- /dev/null +++ b/app/src/main/res/drawable/action_edit_tintable.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/app/src/main/res/drawable/action_filter_tintable.xml b/app/src/main/res/drawable/action_filter_tintable.xml new file mode 100644 index 00000000..d9283e49 --- /dev/null +++ b/app/src/main/res/drawable/action_filter_tintable.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/app/src/main/res/drawable/action_prioritize_tintable.xml b/app/src/main/res/drawable/action_prioritize_tintable.xml new file mode 100644 index 00000000..e6894acc --- /dev/null +++ b/app/src/main/res/drawable/action_prioritize_tintable.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/app/src/main/res/drawable/action_search_tintable.xml b/app/src/main/res/drawable/action_search_tintable.xml new file mode 100644 index 00000000..c8b44ac0 --- /dev/null +++ b/app/src/main/res/drawable/action_search_tintable.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/app/src/main/res/drawable/action_sort_tintable.xml b/app/src/main/res/drawable/action_sort_tintable.xml new file mode 100644 index 00000000..15936ee1 --- /dev/null +++ b/app/src/main/res/drawable/action_sort_tintable.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/app/src/main/res/drawable/action_tags_tintable.xml b/app/src/main/res/drawable/action_tags_tintable.xml new file mode 100644 index 00000000..3fb3e0be --- /dev/null +++ b/app/src/main/res/drawable/action_tags_tintable.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/app/src/main/res/drawable/adaptive_icon.xml b/app/src/main/res/drawable/adaptive_icon.xml new file mode 100644 index 00000000..5bff4a1f --- /dev/null +++ b/app/src/main/res/drawable/adaptive_icon.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/horizontal_rule.xml b/app/src/main/res/drawable/horizontal_rule.xml index eb441e18..fd22d488 100644 --- a/app/src/main/res/drawable/horizontal_rule.xml +++ b/app/src/main/res/drawable/horizontal_rule.xml @@ -25,11 +25,5 @@ You should have received a copy of the GNU General Public License along with Tod - + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_action_add_tintable.xml b/app/src/main/res/drawable/ic_action_add_tintable.xml new file mode 100644 index 00000000..535c63be --- /dev/null +++ b/app/src/main/res/drawable/ic_action_add_tintable.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_action_sync_tintable.xml b/app/src/main/res/drawable/ic_action_sync_tintable.xml new file mode 100644 index 00000000..a017431d --- /dev/null +++ b/app/src/main/res/drawable/ic_action_sync_tintable.xml @@ -0,0 +1,4 @@ + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_arrow_back_black_24dp.xml b/app/src/main/res/drawable/ic_arrow_back_black_24dp.xml new file mode 100644 index 00000000..71d5bbd2 --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_back_black_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_arrow_back_white_24dp.xml b/app/src/main/res/drawable/ic_arrow_back_white_24dp.xml new file mode 100644 index 00000000..71d5bbd2 --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_back_white_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_black_24dp.xml b/app/src/main/res/drawable/ic_menu_black_24dp.xml new file mode 100644 index 00000000..6d9343b3 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_menu_white_24dp.xml b/app/src/main/res/drawable/ic_menu_white_24dp.xml new file mode 100644 index 00000000..de103a67 --- /dev/null +++ b/app/src/main/res/drawable/ic_menu_white_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable/side_sheet_border.xml b/app/src/main/res/drawable/side_sheet_border.xml new file mode 100644 index 00000000..6dbb8ca8 --- /dev/null +++ b/app/src/main/res/drawable/side_sheet_border.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/todotxt_touch_icon.png b/app/src/main/res/drawable/todotxt_touch_icon.png new file mode 100644 index 00000000..b3ecd649 Binary files /dev/null and b/app/src/main/res/drawable/todotxt_touch_icon.png differ diff --git a/app/src/main/res/layout-land/widget.xml b/app/src/main/res/layout-land/widget.xml deleted file mode 100644 index 93396a45..00000000 --- a/app/src/main/res/layout-land/widget.xml +++ /dev/null @@ -1,198 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout-large-land/main.xml b/app/src/main/res/layout-large-land/main.xml deleted file mode 100644 index 6365060a..00000000 --- a/app/src/main/res/layout-large-land/main.xml +++ /dev/null @@ -1,118 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - -