diff --git a/.gitignore b/.gitignore
index 345e61a..bd9cb87 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,9 +2,11 @@
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff:
+local.properties
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/dictionaries
+app/build/
# Sensitive or high-churn files:
.idea/**/dataSources/
@@ -47,3 +49,8 @@ com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
+
+# Binary files
+**.bin
+**.lock
+**.rawproto
diff --git a/README.md b/README.md
index bf78fb0..c47fc1e 100644
--- a/README.md
+++ b/README.md
@@ -3,3 +3,8 @@ Java project; fall semester
## Not Dead by Deadline
An app to help you organize your study life and not die by the time the deadline rolls around.
+
+### Features
+* Adding new homeworks
+* Displaying all current homeworks by subject
+* Displaying all current/nearest deadlines
diff --git a/aFileChooser/aFileChooser.iml b/aFileChooser/aFileChooser.iml
new file mode 100644
index 0000000..0c05f7c
--- /dev/null
+++ b/aFileChooser/aFileChooser.iml
@@ -0,0 +1,123 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ generateDebugSources
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/aFileChooser/build.gradle b/aFileChooser/build.gradle
new file mode 100644
index 0000000..16bf6f8
--- /dev/null
+++ b/aFileChooser/build.gradle
@@ -0,0 +1,21 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 26
+
+ defaultConfig {
+ minSdkVersion 22
+ targetSdkVersion 26
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
+ }
+ }
+}
+
+dependencies {
+ implementation 'com.android.support:support-v4:26.1.0'
+}
diff --git a/aFileChooser/src/main/AndroidManifest.xml b/aFileChooser/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..41d7e58
--- /dev/null
+++ b/aFileChooser/src/main/AndroidManifest.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/aFileChooser/src/main/java/com/ianhanniballake/localstorage/LocalStorageProvider.java b/aFileChooser/src/main/java/com/ianhanniballake/localstorage/LocalStorageProvider.java
new file mode 100644
index 0000000..8060bb8
--- /dev/null
+++ b/aFileChooser/src/main/java/com/ianhanniballake/localstorage/LocalStorageProvider.java
@@ -0,0 +1,234 @@
+
+package com.ianhanniballake.localstorage;
+
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Point;
+import android.os.CancellationSignal;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.provider.DocumentsContract.Document;
+import android.provider.DocumentsContract.Root;
+import android.provider.DocumentsProvider;
+import android.util.Log;
+import android.webkit.MimeTypeMap;
+
+import com.ipaulpro.afilechooser.R;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+public class LocalStorageProvider extends DocumentsProvider {
+
+ // TODO can I fetch build variables from other modules? find out
+ public static final String AUTHORITY = "ru.spbau.group202.notdeadbydeadline";
+
+ /**
+ * Default root projection: everything but Root.COLUMN_MIME_TYPES
+ */
+ private final static String[] DEFAULT_ROOT_PROJECTION = new String[] {
+ Root.COLUMN_ROOT_ID,
+ Root.COLUMN_FLAGS, Root.COLUMN_TITLE, Root.COLUMN_DOCUMENT_ID, Root.COLUMN_ICON,
+ Root.COLUMN_AVAILABLE_BYTES
+ };
+ /**
+ * Default document projection: everything but Document.COLUMN_ICON and
+ * Document.COLUMN_SUMMARY
+ */
+ private final static String[] DEFAULT_DOCUMENT_PROJECTION = new String[] {
+ Document.COLUMN_DOCUMENT_ID,
+ Document.COLUMN_DISPLAY_NAME, Document.COLUMN_FLAGS, Document.COLUMN_MIME_TYPE,
+ Document.COLUMN_SIZE,
+ Document.COLUMN_LAST_MODIFIED
+ };
+
+ @Override
+ public Cursor queryRoots(final String[] projection) throws FileNotFoundException {
+ // Create a cursor with either the requested fields, or the default
+ // projection if "projection" is null.
+ final MatrixCursor result = new MatrixCursor(projection != null ? projection
+ : DEFAULT_ROOT_PROJECTION);
+ // Add Home directory
+ File homeDir = Environment.getExternalStorageDirectory();
+ final MatrixCursor.RowBuilder row = result.newRow();
+ // These columns are required
+ row.add(Root.COLUMN_ROOT_ID, homeDir.getAbsolutePath());
+ row.add(Root.COLUMN_DOCUMENT_ID, homeDir.getAbsolutePath());
+ row.add(Root.COLUMN_TITLE, getContext().getString(R.string.internal_storage));
+ row.add(Root.COLUMN_FLAGS, Root.FLAG_LOCAL_ONLY | Root.FLAG_SUPPORTS_CREATE);
+ row.add(Root.COLUMN_ICON, R.drawable.ic_provider);
+ // These columns are optional
+ row.add(Root.COLUMN_AVAILABLE_BYTES, homeDir.getFreeSpace());
+ // Root.COLUMN_MIME_TYPE is another optional column and useful if you
+ // have multiple roots with different
+ // types of mime types (roots that don't match the requested mime type
+ // are automatically hidden)
+ return result;
+ }
+
+ @Override
+ public String createDocument(final String parentDocumentId, final String mimeType,
+ final String displayName) throws FileNotFoundException {
+ File newFile = new File(parentDocumentId, displayName);
+ try {
+ newFile.createNewFile();
+ return newFile.getAbsolutePath();
+ } catch (IOException e) {
+ Log.e(LocalStorageProvider.class.getSimpleName(), "Error creating new file " + newFile);
+ }
+ return null;
+ }
+
+ @Override
+ public AssetFileDescriptor openDocumentThumbnail(final String documentId, final Point sizeHint,
+ final CancellationSignal signal) throws FileNotFoundException {
+ // Assume documentId points to an image file. Build a thumbnail no
+ // larger than twice the sizeHint
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+ BitmapFactory.decodeFile(documentId, options);
+ final int targetHeight = 2 * sizeHint.y;
+ final int targetWidth = 2 * sizeHint.x;
+ final int height = options.outHeight;
+ final int width = options.outWidth;
+ options.inSampleSize = 1;
+ if (height > targetHeight || width > targetWidth) {
+ final int halfHeight = height / 2;
+ final int halfWidth = width / 2;
+ // Calculate the largest inSampleSize value that is a power of 2 and
+ // keeps both
+ // height and width larger than the requested height and width.
+ while ((halfHeight / options.inSampleSize) > targetHeight
+ || (halfWidth / options.inSampleSize) > targetWidth) {
+ options.inSampleSize *= 2;
+ }
+ }
+ options.inJustDecodeBounds = false;
+ Bitmap bitmap = BitmapFactory.decodeFile(documentId, options);
+ // Write out the thumbnail to a temporary file
+ File tempFile = null;
+ FileOutputStream out = null;
+ try {
+ tempFile = File.createTempFile("thumbnail", null, getContext().getCacheDir());
+ out = new FileOutputStream(tempFile);
+ bitmap.compress(Bitmap.CompressFormat.PNG, 90, out);
+ } catch (IOException e) {
+ Log.e(LocalStorageProvider.class.getSimpleName(), "Error writing thumbnail", e);
+ return null;
+ } finally {
+ if (out != null)
+ try {
+ out.close();
+ } catch (IOException e) {
+ Log.e(LocalStorageProvider.class.getSimpleName(), "Error closing thumbnail", e);
+ }
+ }
+ // It appears the Storage Framework UI caches these results quite
+ // aggressively so there is little reason to
+ // write your own caching layer beyond what you need to return a single
+ // AssetFileDescriptor
+ return new AssetFileDescriptor(ParcelFileDescriptor.open(tempFile,
+ ParcelFileDescriptor.MODE_READ_ONLY), 0,
+ AssetFileDescriptor.UNKNOWN_LENGTH);
+ }
+
+ @Override
+ public Cursor queryChildDocuments(final String parentDocumentId, final String[] projection,
+ final String sortOrder) throws FileNotFoundException {
+ // Create a cursor with either the requested fields, or the default
+ // projection if "projection" is null.
+ final MatrixCursor result = new MatrixCursor(projection != null ? projection
+ : DEFAULT_DOCUMENT_PROJECTION);
+ final File parent = new File(parentDocumentId);
+ for (File file : parent.listFiles()) {
+ // Don't show hidden files/folders
+ if (!file.getName().startsWith(".")) {
+ // Adds the file's display name, MIME type, size, and so on.
+ includeFile(result, file);
+ }
+ }
+ return result;
+ }
+
+ @Override
+ public Cursor queryDocument(final String documentId, final String[] projection)
+ throws FileNotFoundException {
+ // Create a cursor with either the requested fields, or the default
+ // projection if "projection" is null.
+ final MatrixCursor result = new MatrixCursor(projection != null ? projection
+ : DEFAULT_DOCUMENT_PROJECTION);
+ includeFile(result, new File(documentId));
+ return result;
+ }
+
+ private void includeFile(final MatrixCursor result, final File file)
+ throws FileNotFoundException {
+ final MatrixCursor.RowBuilder row = result.newRow();
+ // These columns are required
+ row.add(Document.COLUMN_DOCUMENT_ID, file.getAbsolutePath());
+ row.add(Document.COLUMN_DISPLAY_NAME, file.getName());
+ String mimeType = getDocumentType(file.getAbsolutePath());
+ row.add(Document.COLUMN_MIME_TYPE, mimeType);
+ int flags = file.canWrite() ? Document.FLAG_SUPPORTS_DELETE | Document.FLAG_SUPPORTS_WRITE
+ : 0;
+ // We only show thumbnails for image files - expect a call to
+ // openDocumentThumbnail for each file that has
+ // this flag set
+ if (mimeType.startsWith("image/"))
+ flags |= Document.FLAG_SUPPORTS_THUMBNAIL;
+ row.add(Document.COLUMN_FLAGS, flags);
+ // COLUMN_SIZE is required, but can be null
+ row.add(Document.COLUMN_SIZE, file.length());
+ // These columns are optional
+ row.add(Document.COLUMN_LAST_MODIFIED, file.lastModified());
+ // Document.COLUMN_ICON can be a resource id identifying a custom icon.
+ // The system provides default icons
+ // based on mime type
+ // Document.COLUMN_SUMMARY is optional additional information about the
+ // file
+ }
+
+ @Override
+ public String getDocumentType(final String documentId) throws FileNotFoundException {
+ File file = new File(documentId);
+ if (file.isDirectory())
+ return Document.MIME_TYPE_DIR;
+ // From FileProvider.getType(Uri)
+ final int lastDot = file.getName().lastIndexOf('.');
+ if (lastDot >= 0) {
+ final String extension = file.getName().substring(lastDot + 1);
+ final String mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
+ if (mime != null) {
+ return mime;
+ }
+ }
+ return "application/octet-stream";
+ }
+
+ @Override
+ public void deleteDocument(final String documentId) throws FileNotFoundException {
+ new File(documentId).delete();
+ }
+
+ @Override
+ public ParcelFileDescriptor openDocument(final String documentId, final String mode,
+ final CancellationSignal signal) throws FileNotFoundException {
+ File file = new File(documentId);
+ final boolean isWrite = (mode.indexOf('w') != -1);
+ if (isWrite) {
+ return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE);
+ } else {
+ return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
+ }
+ }
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+}
diff --git a/aFileChooser/src/main/java/com/ipaulpro/afilechooser/FileChooserActivity.java b/aFileChooser/src/main/java/com/ipaulpro/afilechooser/FileChooserActivity.java
new file mode 100644
index 0000000..724cf1d
--- /dev/null
+++ b/aFileChooser/src/main/java/com/ipaulpro/afilechooser/FileChooserActivity.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2013 Paul Burke
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.ipaulpro.afilechooser;
+
+import android.app.ActionBar;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.support.v4.app.FragmentActivity;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentManager.BackStackEntry;
+import android.support.v4.app.FragmentManager.OnBackStackChangedListener;
+import android.support.v4.app.FragmentTransaction;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.Toast;
+
+import java.io.File;
+
+/**
+ * Main Activity that handles the FileListFragments
+ *
+ * @version 2013-06-25
+ * @author paulburke (ipaulpro)
+ */
+public class FileChooserActivity extends FragmentActivity implements
+ OnBackStackChangedListener, FileListFragment.Callbacks {
+
+ public static final String PATH = "path";
+ public static final String EXTERNAL_BASE_PATH = Environment
+ .getExternalStorageDirectory().getAbsolutePath();
+
+ private static final boolean HAS_ACTIONBAR = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
+
+ private FragmentManager mFragmentManager;
+ private BroadcastReceiver mStorageListener = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Toast.makeText(context, R.string.storage_removed, Toast.LENGTH_LONG).show();
+ finishWithResult(null);
+ }
+ };
+
+ private String mPath;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mFragmentManager = getSupportFragmentManager();
+ mFragmentManager.addOnBackStackChangedListener(this);
+
+ if (savedInstanceState == null) {
+ mPath = EXTERNAL_BASE_PATH;
+ addFragment();
+ } else {
+ mPath = savedInstanceState.getString(PATH);
+ }
+
+ setTitle(mPath);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ unregisterStorageListener();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ registerStorageListener();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ outState.putString(PATH, mPath);
+ }
+
+ @Override
+ public void onBackStackChanged() {
+
+ int count = mFragmentManager.getBackStackEntryCount();
+ if (count > 0) {
+ BackStackEntry fragment = mFragmentManager.getBackStackEntryAt(count - 1);
+ mPath = fragment.getName();
+ } else {
+ mPath = EXTERNAL_BASE_PATH;
+ }
+
+ setTitle(mPath);
+ if (HAS_ACTIONBAR)
+ invalidateOptionsMenu();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ if (HAS_ACTIONBAR) {
+ boolean hasBackStack = mFragmentManager.getBackStackEntryCount() > 0;
+
+ ActionBar actionBar = getActionBar();
+ actionBar.setDisplayHomeAsUpEnabled(hasBackStack);
+ actionBar.setHomeButtonEnabled(hasBackStack);
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ mFragmentManager.popBackStack();
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ /**
+ * Add the initial Fragment with given path.
+ */
+ private void addFragment() {
+ FileListFragment fragment = FileListFragment.newInstance(mPath);
+ mFragmentManager.beginTransaction()
+ .add(android.R.id.content, fragment).commit();
+ }
+
+ /**
+ * "Replace" the existing Fragment with a new one using given path. We're
+ * really adding a Fragment to the back stack.
+ *
+ * @param file The file (directory) to display.
+ */
+ private void replaceFragment(File file) {
+ mPath = file.getAbsolutePath();
+
+ FileListFragment fragment = FileListFragment.newInstance(mPath);
+ mFragmentManager.beginTransaction()
+ .replace(android.R.id.content, fragment)
+ .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
+ .addToBackStack(mPath).commit();
+ }
+
+ /**
+ * Finish this Activity with a result code and URI of the selected file.
+ *
+ * @param file The file selected.
+ */
+ private void finishWithResult(File file) {
+ if (file != null) {
+ Uri uri = Uri.fromFile(file);
+ setResult(RESULT_OK, new Intent().setData(uri));
+ finish();
+ } else {
+ setResult(RESULT_CANCELED);
+ finish();
+ }
+ }
+
+ /**
+ * Called when the user selects a File
+ *
+ * @param file The file that was selected
+ */
+ @Override
+ public void onFileSelected(File file) {
+ if (file != null) {
+ if (file.isDirectory()) {
+ replaceFragment(file);
+ } else {
+ finishWithResult(file);
+ }
+ } else {
+ Toast.makeText(FileChooserActivity.this, R.string.error_selecting_file,
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ /**
+ * Register the external storage BroadcastReceiver.
+ */
+ private void registerStorageListener() {
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_MEDIA_REMOVED);
+ registerReceiver(mStorageListener, filter);
+ }
+
+ /**
+ * Unregister the external storage BroadcastReceiver.
+ */
+ private void unregisterStorageListener() {
+ unregisterReceiver(mStorageListener);
+ }
+}
diff --git a/aFileChooser/src/main/java/com/ipaulpro/afilechooser/FileListAdapter.java b/aFileChooser/src/main/java/com/ipaulpro/afilechooser/FileListAdapter.java
new file mode 100644
index 0000000..3480122
--- /dev/null
+++ b/aFileChooser/src/main/java/com/ipaulpro/afilechooser/FileListAdapter.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2012 Paul Burke
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.ipaulpro.afilechooser;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * List adapter for Files.
+ *
+ * @version 2013-12-11
+ * @author paulburke (ipaulpro)
+ */
+public class FileListAdapter extends BaseAdapter {
+
+ private final static int ICON_FOLDER = R.drawable.ic_folder;
+ private final static int ICON_FILE = R.drawable.ic_file;
+
+ private final LayoutInflater mInflater;
+
+ private List mData = new ArrayList();
+
+ public FileListAdapter(Context context) {
+ mInflater = LayoutInflater.from(context);
+ }
+
+ public void add(File file) {
+ mData.add(file);
+ notifyDataSetChanged();
+ }
+
+ public void remove(File file) {
+ mData.remove(file);
+ notifyDataSetChanged();
+ }
+
+ public void insert(File file, int index) {
+ mData.add(index, file);
+ notifyDataSetChanged();
+ }
+
+ public void clear() {
+ mData.clear();
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public File getItem(int position) {
+ return mData.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public int getCount() {
+ return mData.size();
+ }
+
+ public List getListItems() {
+ return mData;
+ }
+
+ /**
+ * Set the list items without notifying on the clear. This prevents loss of
+ * scroll position.
+ *
+ * @param data
+ */
+ public void setListItems(List data) {
+ mData = data;
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ View row = convertView;
+
+ if (row == null)
+ row = mInflater.inflate(R.layout.file, parent, false);
+
+ TextView view = (TextView) row;
+
+ // Get the file at the current position
+ final File file = getItem(position);
+
+ // Set the TextView as the file name
+ view.setText(file.getName());
+
+ // If the item is not a directory, use the file icon
+ int icon = file.isDirectory() ? ICON_FOLDER : ICON_FILE;
+ view.setCompoundDrawablesWithIntrinsicBounds(icon, 0, 0, 0);
+
+ return row;
+ }
+
+}
\ No newline at end of file
diff --git a/aFileChooser/src/main/java/com/ipaulpro/afilechooser/FileListFragment.java b/aFileChooser/src/main/java/com/ipaulpro/afilechooser/FileListFragment.java
new file mode 100644
index 0000000..5da363a
--- /dev/null
+++ b/aFileChooser/src/main/java/com/ipaulpro/afilechooser/FileListFragment.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2013 Paul Burke
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.ipaulpro.afilechooser;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Environment;
+import android.support.v4.app.ListFragment;
+import android.support.v4.app.LoaderManager;
+import android.support.v4.content.Loader;
+import android.view.View;
+import android.widget.ListView;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Fragment that displays a list of Files in a given path.
+ *
+ * @version 2013-12-11
+ * @author paulburke (ipaulpro)
+ */
+public class FileListFragment extends ListFragment implements
+ LoaderManager.LoaderCallbacks> {
+
+ /**
+ * Interface to listen for events.
+ */
+ public interface Callbacks {
+ /**
+ * Called when a file is selected from the list.
+ *
+ * @param file The file selected
+ */
+ public void onFileSelected(File file);
+ }
+
+ private static final int LOADER_ID = 0;
+
+ private FileListAdapter mAdapter;
+ private String mPath;
+
+ private Callbacks mListener;
+
+ /**
+ * Create a new instance with the given file path.
+ *
+ * @param path The absolute path of the file (directory) to display.
+ * @return A new Fragment with the given file path.
+ */
+ public static FileListFragment newInstance(String path) {
+ FileListFragment fragment = new FileListFragment();
+ Bundle args = new Bundle();
+ args.putString(FileChooserActivity.PATH, path);
+ fragment.setArguments(args);
+
+ return fragment;
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+
+ try {
+ mListener = (Callbacks) activity;
+ } catch (ClassCastException e) {
+ throw new ClassCastException(activity.toString()
+ + " must implement FileListFragment.Callbacks");
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mAdapter = new FileListAdapter(getActivity());
+ mPath = getArguments() != null ? getArguments().getString(
+ FileChooserActivity.PATH) : Environment
+ .getExternalStorageDirectory().getAbsolutePath();
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ setEmptyText(getString(R.string.empty_directory));
+ setListAdapter(mAdapter);
+ setListShown(false);
+
+ getLoaderManager().initLoader(LOADER_ID, null, this);
+
+ super.onActivityCreated(savedInstanceState);
+ }
+
+ @Override
+ public void onListItemClick(ListView l, View v, int position, long id) {
+ FileListAdapter adapter = (FileListAdapter) l.getAdapter();
+ if (adapter != null) {
+ File file = (File) adapter.getItem(position);
+ mPath = file.getAbsolutePath();
+ mListener.onFileSelected(file);
+ }
+ }
+
+ @Override
+ public Loader> onCreateLoader(int id, Bundle args) {
+ return new FileLoader(getActivity(), mPath);
+ }
+
+ @Override
+ public void onLoadFinished(Loader> loader, List data) {
+ mAdapter.setListItems(data);
+
+ if (isResumed())
+ setListShown(true);
+ else
+ setListShownNoAnimation(true);
+ }
+
+ @Override
+ public void onLoaderReset(Loader> loader) {
+ mAdapter.clear();
+ }
+}
diff --git a/aFileChooser/src/main/java/com/ipaulpro/afilechooser/FileLoader.java b/aFileChooser/src/main/java/com/ipaulpro/afilechooser/FileLoader.java
new file mode 100644
index 0000000..f8903ac
--- /dev/null
+++ b/aFileChooser/src/main/java/com/ipaulpro/afilechooser/FileLoader.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2013 Paul Burke
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.ipaulpro.afilechooser;
+
+import android.content.Context;
+import android.os.FileObserver;
+import android.support.v4.content.AsyncTaskLoader;
+
+import com.ipaulpro.afilechooser.utils.FileUtils;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Loader that returns a list of Files in a given file path.
+ *
+ * @version 2013-12-11
+ * @author paulburke (ipaulpro)
+ */
+public class FileLoader extends AsyncTaskLoader> {
+
+ private static final int FILE_OBSERVER_MASK = FileObserver.CREATE
+ | FileObserver.DELETE | FileObserver.DELETE_SELF
+ | FileObserver.MOVED_FROM | FileObserver.MOVED_TO
+ | FileObserver.MODIFY | FileObserver.MOVE_SELF;
+
+ private FileObserver mFileObserver;
+
+ private List mData;
+ private String mPath;
+
+ public FileLoader(Context context, String path) {
+ super(context);
+ this.mPath = path;
+ }
+
+ @Override
+ public List loadInBackground() {
+
+ ArrayList list = new ArrayList();
+
+ // Current directory File instance
+ final File pathDir = new File(mPath);
+
+ // List file in this directory with the directory filter
+ final File[] dirs = pathDir.listFiles(FileUtils.sDirFilter);
+ if (dirs != null) {
+ // Sort the folders alphabetically
+ Arrays.sort(dirs, FileUtils.sComparator);
+ // Add each folder to the File list for the list adapter
+ for (File dir : dirs)
+ list.add(dir);
+ }
+
+ // List file in this directory with the file filter
+ final File[] files = pathDir.listFiles(FileUtils.sFileFilter);
+ if (files != null) {
+ // Sort the files alphabetically
+ Arrays.sort(files, FileUtils.sComparator);
+ // Add each file to the File list for the list adapter
+ for (File file : files)
+ list.add(file);
+ }
+
+ return list;
+ }
+
+ @Override
+ public void deliverResult(List data) {
+ if (isReset()) {
+ onReleaseResources(data);
+ return;
+ }
+
+ List oldData = mData;
+ mData = data;
+
+ if (isStarted())
+ super.deliverResult(data);
+
+ if (oldData != null && oldData != data)
+ onReleaseResources(oldData);
+ }
+
+ @Override
+ protected void onStartLoading() {
+ if (mData != null)
+ deliverResult(mData);
+
+ if (mFileObserver == null) {
+ mFileObserver = new FileObserver(mPath, FILE_OBSERVER_MASK) {
+ @Override
+ public void onEvent(int event, String path) {
+ onContentChanged();
+ }
+ };
+ }
+ mFileObserver.startWatching();
+
+ if (takeContentChanged() || mData == null)
+ forceLoad();
+ }
+
+ @Override
+ protected void onStopLoading() {
+ cancelLoad();
+ }
+
+ @Override
+ protected void onReset() {
+ onStopLoading();
+
+ if (mData != null) {
+ onReleaseResources(mData);
+ mData = null;
+ }
+ }
+
+ @Override
+ public void onCanceled(List data) {
+ super.onCanceled(data);
+
+ onReleaseResources(data);
+ }
+
+ protected void onReleaseResources(List data) {
+
+ if (mFileObserver != null) {
+ mFileObserver.stopWatching();
+ mFileObserver = null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/aFileChooser/src/main/java/com/ipaulpro/afilechooser/utils/FileUtils.java b/aFileChooser/src/main/java/com/ipaulpro/afilechooser/utils/FileUtils.java
new file mode 100644
index 0000000..8320f7a
--- /dev/null
+++ b/aFileChooser/src/main/java/com/ipaulpro/afilechooser/utils/FileUtils.java
@@ -0,0 +1,528 @@
+/*
+ * Copyright (C) 2007-2008 OpenIntents.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.ipaulpro.afilechooser.utils;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Environment;
+import android.provider.DocumentsContract;
+import android.provider.MediaStore;
+import android.util.Log;
+import android.webkit.MimeTypeMap;
+
+import com.ianhanniballake.localstorage.LocalStorageProvider;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.text.DecimalFormat;
+import java.util.Comparator;
+
+/**
+ * @version 2009-07-03
+ * @author Peli
+ * @version 2013-12-11
+ * @author paulburke (ipaulpro)
+ */
+public class FileUtils {
+ private FileUtils() {} //private constructor to enforce Singleton pattern
+
+ /** TAG for log messages. */
+ static final String TAG = "FileUtils";
+ private static final boolean DEBUG = false; // Set to true to enable logging
+
+ public static final String MIME_TYPE_AUDIO = "audio/*";
+ public static final String MIME_TYPE_TEXT = "text/*";
+ public static final String MIME_TYPE_IMAGE = "image/*";
+ public static final String MIME_TYPE_VIDEO = "video/*";
+ public static final String MIME_TYPE_APP = "application/*";
+
+ public static final String HIDDEN_PREFIX = ".";
+
+ /**
+ * Gets the extension of a file name, like ".png" or ".jpg".
+ *
+ * @param uri
+ * @return Extension including the dot("."); "" if there is no extension;
+ * null if uri was null.
+ */
+ public static String getExtension(String uri) {
+ if (uri == null) {
+ return null;
+ }
+
+ int dot = uri.lastIndexOf(".");
+ if (dot >= 0) {
+ return uri.substring(dot);
+ } else {
+ // No extension.
+ return "";
+ }
+ }
+
+ /**
+ * @return Whether the URI is a local one.
+ */
+ public static boolean isLocal(String url) {
+ if (url != null && !url.startsWith("http://") && !url.startsWith("https://")) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @return True if Uri is a MediaStore Uri.
+ * @author paulburke
+ */
+ public static boolean isMediaUri(Uri uri) {
+ return "media".equalsIgnoreCase(uri.getAuthority());
+ }
+
+ /**
+ * Convert File into Uri.
+ *
+ * @param file
+ * @return uri
+ */
+ public static Uri getUri(File file) {
+ if (file != null) {
+ return Uri.fromFile(file);
+ }
+ return null;
+ }
+
+ /**
+ * Returns the path only (without file name).
+ *
+ * @param file
+ * @return
+ */
+ public static File getPathWithoutFilename(File file) {
+ if (file != null) {
+ if (file.isDirectory()) {
+ // no file to be split off. Return everything
+ return file;
+ } else {
+ String filename = file.getName();
+ String filepath = file.getAbsolutePath();
+
+ // Construct path without file name.
+ String pathwithoutname = filepath.substring(0,
+ filepath.length() - filename.length());
+ if (pathwithoutname.endsWith("/")) {
+ pathwithoutname = pathwithoutname.substring(0, pathwithoutname.length() - 1);
+ }
+ return new File(pathwithoutname);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @return The MIME type for the given file.
+ */
+ public static String getMimeType(File file) {
+
+ String extension = getExtension(file.getName());
+
+ if (extension.length() > 0)
+ return MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.substring(1));
+
+ return "application/octet-stream";
+ }
+
+ /**
+ * @return The MIME type for the give Uri.
+ */
+ public static String getMimeType(Context context, Uri uri) {
+ File file = new File(getPath(context, uri));
+ return getMimeType(file);
+ }
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is {@link LocalStorageProvider}.
+ * @author paulburke
+ */
+ public static boolean isLocalStorageDocument(Uri uri) {
+ return LocalStorageProvider.AUTHORITY.equals(uri.getAuthority());
+ }
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is ExternalStorageProvider.
+ * @author paulburke
+ */
+ public static boolean isExternalStorageDocument(Uri uri) {
+ return "com.android.externalstorage.documents".equals(uri.getAuthority());
+ }
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is DownloadsProvider.
+ * @author paulburke
+ */
+ public static boolean isDownloadsDocument(Uri uri) {
+ return "com.android.providers.downloads.documents".equals(uri.getAuthority());
+ }
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is MediaProvider.
+ * @author paulburke
+ */
+ public static boolean isMediaDocument(Uri uri) {
+ return "com.android.providers.media.documents".equals(uri.getAuthority());
+ }
+
+ /**
+ * @param uri The Uri to check.
+ * @return Whether the Uri authority is Google Photos.
+ */
+ public static boolean isGooglePhotosUri(Uri uri) {
+ return "com.google.android.apps.photos.content".equals(uri.getAuthority());
+ }
+
+ /**
+ * Get the value of the data column for this Uri. This is useful for
+ * MediaStore Uris, and other file-based ContentProviders.
+ *
+ * @param context The context.
+ * @param uri The Uri to query.
+ * @param selection (Optional) Filter used in the query.
+ * @param selectionArgs (Optional) Selection arguments used in the query.
+ * @return The value of the _data column, which is typically a file path.
+ * @author paulburke
+ */
+ public static String getDataColumn(Context context, Uri uri, String selection,
+ String[] selectionArgs) {
+
+ Cursor cursor = null;
+ final String column = "_data";
+ final String[] projection = {
+ column
+ };
+
+ try {
+ cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
+ null);
+ if (cursor != null && cursor.moveToFirst()) {
+ if (DEBUG)
+ DatabaseUtils.dumpCursor(cursor);
+
+ final int column_index = cursor.getColumnIndexOrThrow(column);
+ return cursor.getString(column_index);
+ }
+ } finally {
+ if (cursor != null)
+ cursor.close();
+ }
+ return null;
+ }
+
+ /**
+ * Get a file path from a Uri. This will get the the path for Storage Access
+ * Framework Documents, as well as the _data field for the MediaStore and
+ * other file-based ContentProviders.
+ *
+ * Callers should check whether the path is local before assuming it
+ * represents a local file.
+ *
+ * @param context The context.
+ * @param uri The Uri to query.
+ * @see #isLocal(String)
+ * @see #getFile(Context, Uri)
+ * @author paulburke
+ */
+ public static String getPath(final Context context, final Uri uri) {
+
+ if (DEBUG)
+ Log.d(TAG + " File -",
+ "Authority: " + uri.getAuthority() +
+ ", Fragment: " + uri.getFragment() +
+ ", Port: " + uri.getPort() +
+ ", Query: " + uri.getQuery() +
+ ", Scheme: " + uri.getScheme() +
+ ", Host: " + uri.getHost() +
+ ", Segments: " + uri.getPathSegments().toString()
+ );
+
+ final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
+
+ // DocumentProvider
+ if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
+ // LocalStorageProvider
+ if (isLocalStorageDocument(uri)) {
+ // The path is the id
+ return DocumentsContract.getDocumentId(uri);
+ }
+ // ExternalStorageProvider
+ else if (isExternalStorageDocument(uri)) {
+ final String docId = DocumentsContract.getDocumentId(uri);
+ final String[] split = docId.split(":");
+ final String type = split[0];
+
+ if ("primary".equalsIgnoreCase(type)) {
+ return Environment.getExternalStorageDirectory() + "/" + split[1];
+ }
+
+ // TODO handle non-primary volumes
+ }
+ // DownloadsProvider
+ else if (isDownloadsDocument(uri)) {
+
+ final String id = DocumentsContract.getDocumentId(uri);
+ final Uri contentUri = ContentUris.withAppendedId(
+ Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
+
+ return getDataColumn(context, contentUri, null, null);
+ }
+ // MediaProvider
+ else if (isMediaDocument(uri)) {
+ final String docId = DocumentsContract.getDocumentId(uri);
+ final String[] split = docId.split(":");
+ final String type = split[0];
+
+ Uri contentUri = null;
+ if ("image".equals(type)) {
+ contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
+ } else if ("video".equals(type)) {
+ contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
+ } else if ("audio".equals(type)) {
+ contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
+ }
+
+ final String selection = "_id=?";
+ final String[] selectionArgs = new String[] {
+ split[1]
+ };
+
+ return getDataColumn(context, contentUri, selection, selectionArgs);
+ }
+ }
+ // MediaStore (and general)
+ else if ("content".equalsIgnoreCase(uri.getScheme())) {
+
+ // Return the remote address
+ if (isGooglePhotosUri(uri))
+ return uri.getLastPathSegment();
+
+ return getDataColumn(context, uri, null, null);
+ }
+ // File
+ else if ("file".equalsIgnoreCase(uri.getScheme())) {
+ return uri.getPath();
+ }
+
+ return null;
+ }
+
+ /**
+ * Convert Uri into File, if possible.
+ *
+ * @return file A local file that the Uri was pointing to, or null if the
+ * Uri is unsupported or pointed to a remote resource.
+ * @see #getPath(Context, Uri)
+ * @author paulburke
+ */
+ public static File getFile(Context context, Uri uri) {
+ if (uri != null) {
+ String path = getPath(context, uri);
+ if (path != null && isLocal(path)) {
+ return new File(path);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the file size in a human-readable string.
+ *
+ * @param size
+ * @return
+ * @author paulburke
+ */
+ public static String getReadableFileSize(int size) {
+ final int BYTES_IN_KILOBYTES = 1024;
+ final DecimalFormat dec = new DecimalFormat("###.#");
+ final String KILOBYTES = " KB";
+ final String MEGABYTES = " MB";
+ final String GIGABYTES = " GB";
+ float fileSize = 0;
+ String suffix = KILOBYTES;
+
+ if (size > BYTES_IN_KILOBYTES) {
+ fileSize = size / BYTES_IN_KILOBYTES;
+ if (fileSize > BYTES_IN_KILOBYTES) {
+ fileSize = fileSize / BYTES_IN_KILOBYTES;
+ if (fileSize > BYTES_IN_KILOBYTES) {
+ fileSize = fileSize / BYTES_IN_KILOBYTES;
+ suffix = GIGABYTES;
+ } else {
+ suffix = MEGABYTES;
+ }
+ }
+ }
+ return String.valueOf(dec.format(fileSize) + suffix);
+ }
+
+ /**
+ * Attempt to retrieve the thumbnail of given File from the MediaStore. This
+ * should not be called on the UI thread.
+ *
+ * @param context
+ * @param file
+ * @return
+ * @author paulburke
+ */
+ public static Bitmap getThumbnail(Context context, File file) {
+ return getThumbnail(context, getUri(file), getMimeType(file));
+ }
+
+ /**
+ * Attempt to retrieve the thumbnail of given Uri from the MediaStore. This
+ * should not be called on the UI thread.
+ *
+ * @param context
+ * @param uri
+ * @return
+ * @author paulburke
+ */
+ public static Bitmap getThumbnail(Context context, Uri uri) {
+ return getThumbnail(context, uri, getMimeType(context, uri));
+ }
+
+ /**
+ * Attempt to retrieve the thumbnail of given Uri from the MediaStore. This
+ * should not be called on the UI thread.
+ *
+ * @param context
+ * @param uri
+ * @param mimeType
+ * @return
+ * @author paulburke
+ */
+ public static Bitmap getThumbnail(Context context, Uri uri, String mimeType) {
+ if (DEBUG)
+ Log.d(TAG, "Attempting to get thumbnail");
+
+ if (!isMediaUri(uri)) {
+ Log.e(TAG, "You can only retrieve thumbnails for images and videos.");
+ return null;
+ }
+
+ Bitmap bm = null;
+ if (uri != null) {
+ final ContentResolver resolver = context.getContentResolver();
+ Cursor cursor = null;
+ try {
+ cursor = resolver.query(uri, null, null, null, null);
+ if (cursor.moveToFirst()) {
+ final int id = cursor.getInt(0);
+ if (DEBUG)
+ Log.d(TAG, "Got thumb ID: " + id);
+
+ if (mimeType.contains("video")) {
+ bm = MediaStore.Video.Thumbnails.getThumbnail(
+ resolver,
+ id,
+ MediaStore.Video.Thumbnails.MINI_KIND,
+ null);
+ }
+ else if (mimeType.contains(FileUtils.MIME_TYPE_IMAGE)) {
+ bm = MediaStore.Images.Thumbnails.getThumbnail(
+ resolver,
+ id,
+ MediaStore.Images.Thumbnails.MINI_KIND,
+ null);
+ }
+ }
+ } catch (Exception e) {
+ if (DEBUG)
+ Log.e(TAG, "getThumbnail", e);
+ } finally {
+ if (cursor != null)
+ cursor.close();
+ }
+ }
+ return bm;
+ }
+
+ /**
+ * File and folder comparator. TODO Expose sorting option method
+ *
+ * @author paulburke
+ */
+ public static Comparator sComparator = new Comparator() {
+ @Override
+ public int compare(File f1, File f2) {
+ // Sort alphabetically by lower case, which is much cleaner
+ return f1.getName().toLowerCase().compareTo(
+ f2.getName().toLowerCase());
+ }
+ };
+
+ /**
+ * File (not directories) filter.
+ *
+ * @author paulburke
+ */
+ public static FileFilter sFileFilter = new FileFilter() {
+ @Override
+ public boolean accept(File file) {
+ final String fileName = file.getName();
+ // Return files only (not directories) and skip hidden files
+ return file.isFile() && !fileName.startsWith(HIDDEN_PREFIX);
+ }
+ };
+
+ /**
+ * Folder (directories) filter.
+ *
+ * @author paulburke
+ */
+ public static FileFilter sDirFilter = new FileFilter() {
+ @Override
+ public boolean accept(File file) {
+ final String fileName = file.getName();
+ // Return directories only and skip hidden directories
+ return file.isDirectory() && !fileName.startsWith(HIDDEN_PREFIX);
+ }
+ };
+
+ /**
+ * Get the Intent for selecting content to be used in an Intent Chooser.
+ *
+ * @return The intent for opening a file with Intent.createChooser()
+ * @author paulburke
+ */
+ public static Intent createGetContentIntent() {
+ // Implicitly allow the user to select a particular kind of data
+ final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ // The MIME data type filter
+ intent.setType("*/*");
+ // Only return URIs that can be opened with ContentResolver
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ return intent;
+ }
+}
diff --git a/aFileChooser/src/main/res/drawable-hdpi/ic_chooser.png b/aFileChooser/src/main/res/drawable-hdpi/ic_chooser.png
new file mode 100644
index 0000000..da35bf2
Binary files /dev/null and b/aFileChooser/src/main/res/drawable-hdpi/ic_chooser.png differ
diff --git a/aFileChooser/src/main/res/drawable-hdpi/ic_file.png b/aFileChooser/src/main/res/drawable-hdpi/ic_file.png
new file mode 100644
index 0000000..39ef158
Binary files /dev/null and b/aFileChooser/src/main/res/drawable-hdpi/ic_file.png differ
diff --git a/aFileChooser/src/main/res/drawable-hdpi/ic_folder.png b/aFileChooser/src/main/res/drawable-hdpi/ic_folder.png
new file mode 100644
index 0000000..b73961c
Binary files /dev/null and b/aFileChooser/src/main/res/drawable-hdpi/ic_folder.png differ
diff --git a/aFileChooser/src/main/res/drawable-hdpi/ic_provider.png b/aFileChooser/src/main/res/drawable-hdpi/ic_provider.png
new file mode 100644
index 0000000..e236c01
Binary files /dev/null and b/aFileChooser/src/main/res/drawable-hdpi/ic_provider.png differ
diff --git a/aFileChooser/src/main/res/drawable-mdpi/ic_chooser.png b/aFileChooser/src/main/res/drawable-mdpi/ic_chooser.png
new file mode 100644
index 0000000..68b3534
Binary files /dev/null and b/aFileChooser/src/main/res/drawable-mdpi/ic_chooser.png differ
diff --git a/aFileChooser/src/main/res/drawable-mdpi/ic_file.png b/aFileChooser/src/main/res/drawable-mdpi/ic_file.png
new file mode 100644
index 0000000..2ac7a07
Binary files /dev/null and b/aFileChooser/src/main/res/drawable-mdpi/ic_file.png differ
diff --git a/aFileChooser/src/main/res/drawable-mdpi/ic_folder.png b/aFileChooser/src/main/res/drawable-mdpi/ic_folder.png
new file mode 100644
index 0000000..727353f
Binary files /dev/null and b/aFileChooser/src/main/res/drawable-mdpi/ic_folder.png differ
diff --git a/aFileChooser/src/main/res/drawable-mdpi/ic_provider.png b/aFileChooser/src/main/res/drawable-mdpi/ic_provider.png
new file mode 100644
index 0000000..5fbbe81
Binary files /dev/null and b/aFileChooser/src/main/res/drawable-mdpi/ic_provider.png differ
diff --git a/aFileChooser/src/main/res/drawable-xhdpi/ic_chooser.png b/aFileChooser/src/main/res/drawable-xhdpi/ic_chooser.png
new file mode 100644
index 0000000..a2e38a4
Binary files /dev/null and b/aFileChooser/src/main/res/drawable-xhdpi/ic_chooser.png differ
diff --git a/aFileChooser/src/main/res/drawable-xhdpi/ic_file.png b/aFileChooser/src/main/res/drawable-xhdpi/ic_file.png
new file mode 100644
index 0000000..3d9c773
Binary files /dev/null and b/aFileChooser/src/main/res/drawable-xhdpi/ic_file.png differ
diff --git a/aFileChooser/src/main/res/drawable-xhdpi/ic_folder.png b/aFileChooser/src/main/res/drawable-xhdpi/ic_folder.png
new file mode 100644
index 0000000..d539a3a
Binary files /dev/null and b/aFileChooser/src/main/res/drawable-xhdpi/ic_folder.png differ
diff --git a/aFileChooser/src/main/res/drawable-xhdpi/ic_provider.png b/aFileChooser/src/main/res/drawable-xhdpi/ic_provider.png
new file mode 100644
index 0000000..48909ef
Binary files /dev/null and b/aFileChooser/src/main/res/drawable-xhdpi/ic_provider.png differ
diff --git a/aFileChooser/src/main/res/drawable-xxhdpi/ic_chooser.png b/aFileChooser/src/main/res/drawable-xxhdpi/ic_chooser.png
new file mode 100644
index 0000000..0395102
Binary files /dev/null and b/aFileChooser/src/main/res/drawable-xxhdpi/ic_chooser.png differ
diff --git a/aFileChooser/src/main/res/drawable-xxhdpi/ic_file.png b/aFileChooser/src/main/res/drawable-xxhdpi/ic_file.png
new file mode 100644
index 0000000..1102307
Binary files /dev/null and b/aFileChooser/src/main/res/drawable-xxhdpi/ic_file.png differ
diff --git a/aFileChooser/src/main/res/drawable-xxhdpi/ic_folder.png b/aFileChooser/src/main/res/drawable-xxhdpi/ic_folder.png
new file mode 100644
index 0000000..f0735bb
Binary files /dev/null and b/aFileChooser/src/main/res/drawable-xxhdpi/ic_folder.png differ
diff --git a/aFileChooser/src/main/res/drawable-xxhdpi/ic_provider.png b/aFileChooser/src/main/res/drawable-xxhdpi/ic_provider.png
new file mode 100644
index 0000000..2aa8839
Binary files /dev/null and b/aFileChooser/src/main/res/drawable-xxhdpi/ic_provider.png differ
diff --git a/aFileChooser/src/main/res/layout/file.xml b/aFileChooser/src/main/res/layout/file.xml
new file mode 100644
index 0000000..c07cf49
--- /dev/null
+++ b/aFileChooser/src/main/res/layout/file.xml
@@ -0,0 +1,20 @@
+
+
+
diff --git a/aFileChooser/src/main/res/values-ca/strings.xml b/aFileChooser/src/main/res/values-ca/strings.xml
new file mode 100644
index 0000000..df8dc7e
--- /dev/null
+++ b/aFileChooser/src/main/res/values-ca/strings.xml
@@ -0,0 +1,8 @@
+
+
+
+ Carpeta buida
+ S\'ha tret o desmuntat l\'emmagatzematge.
+ Seleccioneu un fitxer
+ Error en seleccionar el fitxer
+
diff --git a/aFileChooser/src/main/res/values-de/strings.xml b/aFileChooser/src/main/res/values-de/strings.xml
new file mode 100644
index 0000000..d6f6058
--- /dev/null
+++ b/aFileChooser/src/main/res/values-de/strings.xml
@@ -0,0 +1,7 @@
+
+
+ Leerer Ordner
+ Speicher wurde entferntet.
+ Wähle eine Datei
+ Fehler beim Öffnen der Datei
+
\ No newline at end of file
diff --git a/aFileChooser/src/main/res/values-es/strings.xml b/aFileChooser/src/main/res/values-es/strings.xml
new file mode 100644
index 0000000..005247f
--- /dev/null
+++ b/aFileChooser/src/main/res/values-es/strings.xml
@@ -0,0 +1,8 @@
+
+
+
+ Directorio vacío
+ Se ha retirado o desmontado el almacenamiento.
+ Seleccione un archivo
+ Error al seleccionar el archivo
+
diff --git a/aFileChooser/src/main/res/values-fr/strings.xml b/aFileChooser/src/main/res/values-fr/strings.xml
new file mode 100644
index 0000000..6077476
--- /dev/null
+++ b/aFileChooser/src/main/res/values-fr/strings.xml
@@ -0,0 +1,8 @@
+
+
+
+ Dossier vide
+ Le stockage a été enlevé ou démonté.
+ Sélectionnez un fichier
+ Erreur lors de la sélection du fichier
+
diff --git a/aFileChooser/src/main/res/values-ga/strings.xml b/aFileChooser/src/main/res/values-ga/strings.xml
new file mode 100644
index 0000000..ee9185d
--- /dev/null
+++ b/aFileChooser/src/main/res/values-ga/strings.xml
@@ -0,0 +1,8 @@
+
+
+
+ Comhadlann fholamh
+ Baineadh amach an gléas stórála nó dínascadh é.
+ Roghnaigh comhad
+ Tharla botún fad is a bhí comhad á roghnú
+
diff --git a/aFileChooser/src/main/res/values-it/strings.xml b/aFileChooser/src/main/res/values-it/strings.xml
new file mode 100644
index 0000000..d4a7d36
--- /dev/null
+++ b/aFileChooser/src/main/res/values-it/strings.xml
@@ -0,0 +1,8 @@
+
+
+
+ Directory vuota
+ Lo spazio di archiviazione è stato rimosso o smontato.
+ Selezionare un file
+ Errore nel selezionare il File
+
diff --git a/aFileChooser/src/main/res/values-ko/strings.xml b/aFileChooser/src/main/res/values-ko/strings.xml
new file mode 100644
index 0000000..dfe0e7e
--- /dev/null
+++ b/aFileChooser/src/main/res/values-ko/strings.xml
@@ -0,0 +1,8 @@
+
+
+
+ 빈 디렉토리
+ 저장소가 제거되었습니다.
+ 파일 선택
+ 파일 선택 오류
+
diff --git a/aFileChooser/src/main/res/values-pl/strings.xml b/aFileChooser/src/main/res/values-pl/strings.xml
new file mode 100644
index 0000000..fd0077d
--- /dev/null
+++ b/aFileChooser/src/main/res/values-pl/strings.xml
@@ -0,0 +1,8 @@
+
+
+
+ Pusty katalog
+ Pamięć została usunięta lub odmontowana.
+ Wybierz plik
+ Błąd, podczas wybierania pliku
+
diff --git a/aFileChooser/src/main/res/values-pt-rBR/strings.xml b/aFileChooser/src/main/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000..4c8f005
--- /dev/null
+++ b/aFileChooser/src/main/res/values-pt-rBR/strings.xml
@@ -0,0 +1,8 @@
+
+
+
+ Pasta Vazia
+ Unidade externa removida ou não preparada.
+ Selecione um Arquivo
+ Erro ao selecionar o Arquivo
+
diff --git a/aFileChooser/src/main/res/values-ru/strings.xml b/aFileChooser/src/main/res/values-ru/strings.xml
new file mode 100644
index 0000000..8467250
--- /dev/null
+++ b/aFileChooser/src/main/res/values-ru/strings.xml
@@ -0,0 +1,8 @@
+
+
+
+ Пустая папка
+ Storage was removed or unmounted.
+ Выберите файл
+ Ошибка при выборе файла
+
diff --git a/aFileChooser/src/main/res/values-v11/strings.xml b/aFileChooser/src/main/res/values-v11/strings.xml
new file mode 100644
index 0000000..9b114a3
--- /dev/null
+++ b/aFileChooser/src/main/res/values-v11/strings.xml
@@ -0,0 +1,19 @@
+
+
+
+ Choose a file
+
\ No newline at end of file
diff --git a/aFileChooser/src/main/res/values-v19/bool.xml b/aFileChooser/src/main/res/values-v19/bool.xml
new file mode 100644
index 0000000..5fc0a1d
--- /dev/null
+++ b/aFileChooser/src/main/res/values-v19/bool.xml
@@ -0,0 +1,7 @@
+
+
+
+ false
+ true
+
+
\ No newline at end of file
diff --git a/aFileChooser/src/main/res/values/bool.xml b/aFileChooser/src/main/res/values/bool.xml
new file mode 100644
index 0000000..5d09580
--- /dev/null
+++ b/aFileChooser/src/main/res/values/bool.xml
@@ -0,0 +1,7 @@
+
+
+
+ true
+ false
+
+
\ No newline at end of file
diff --git a/aFileChooser/src/main/res/values/dimens.xml b/aFileChooser/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..a991784
--- /dev/null
+++ b/aFileChooser/src/main/res/values/dimens.xml
@@ -0,0 +1,20 @@
+
+
+
+ 0dp
+ 16dp
+
\ No newline at end of file
diff --git a/aFileChooser/src/main/res/values/strings.xml b/aFileChooser/src/main/res/values/strings.xml
new file mode 100644
index 0000000..4c062df
--- /dev/null
+++ b/aFileChooser/src/main/res/values/strings.xml
@@ -0,0 +1,23 @@
+
+
+
+ Empty Directory
+ Storage was removed or unmounted.
+ Select a file
+ Error selecting File
+ Internal storage
+
\ No newline at end of file
diff --git a/aFileChooser/src/main/res/values/styles.xml b/aFileChooser/src/main/res/values/styles.xml
new file mode 100644
index 0000000..7f1e43d
--- /dev/null
+++ b/aFileChooser/src/main/res/values/styles.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/aFileChooser/src/main/res/xml/mimetypes.xml b/aFileChooser/src/main/res/xml/mimetypes.xml
new file mode 100644
index 0000000..062435f
--- /dev/null
+++ b/aFileChooser/src/main/res/xml/mimetypes.xml
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/app.iml b/app/app.iml
new file mode 100644
index 0000000..127c9f2
--- /dev/null
+++ b/app/app.iml
@@ -0,0 +1,160 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ generateDebugSources
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..522f589
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,46 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 26
+ defaultConfig {
+ applicationId "ru.spbau.group202.notdeadbydeadline"
+ minSdkVersion 22
+ targetSdkVersion 26
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ multiDexEnabled true
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ lintOptions {
+ abortOnError false
+ }
+}
+
+dependencies {
+ implementation "com.google.code.gson:gson:2.8.0"
+ implementation group: 'org.jsoup', name: 'jsoup', version: '1.7.2'
+ implementation group: 'org.apache.commons', name: 'commons-collections4', version: '4.0'
+ implementation group: 'commons-io', name: 'commons-io', version: '2.4'
+ implementation 'com.android.support:appcompat-v7:26.1.0'
+ implementation 'com.android.support:support-v4:26.1.0'
+ implementation 'com.android.support:design:26.1.0'
+ implementation 'com.android.support.constraint:constraint-layout:1.0.2'
+ 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'
+ implementation 'org.jetbrains:annotations-java5:15.0'
+ implementation 'net.danlew:android.joda:2.9.9.1'
+ implementation 'com.android.support:multidex:1.0.2'
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation project(":aFileChooser")
+}
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..f1b4245
--- /dev/null
+++ b/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# 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/app/src/androidTest/java/ru/spbau/group202/notdeadbydeadline/ExampleInstrumentedTest.java b/app/src/androidTest/java/ru/spbau/group202/notdeadbydeadline/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..af8e055
--- /dev/null
+++ b/app/src/androidTest/java/ru/spbau/group202/notdeadbydeadline/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package ru.spbau.group202.notdeadbydeadline;
+
+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("ru.spbau.group202.notdeadbydeadline", appContext.getPackageName());
+ }
+}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..66e8ed8
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,119 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/ClassDatabaseController.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/ClassDatabaseController.java
new file mode 100644
index 0000000..8101249
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/ClassDatabaseController.java
@@ -0,0 +1,158 @@
+package ru.spbau.group202.notdeadbydeadline.controller;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import ru.spbau.group202.notdeadbydeadline.model.Class;
+import ru.spbau.group202.notdeadbydeadline.model.WeekParityEnum;
+
+public class ClassDatabaseController extends SQLiteOpenHelper {
+ private static final String DATABASE_NAME = "Schedule";
+ private static final int DATABASE_VERSION = 1;
+ private static final String COLUMN_NAME_ID = "ID";
+ private static final String COLUMN_NAME_SUBJECT = "SUBJECT";
+ private static final String COLUMN_NAME_DAY_OF_WEEK = "DAY_OF_WEEK";
+ private static final String COLUMN_NAME_HOUR = "HOUR";
+ private static final String COLUMN_NAME_MINUTE = "MINUTE";
+ private static final String COLUMN_NAME_WEEK_PARITY = "WEEK_PARITY";
+ private static final String COLUMN_NAME_AUDITORIUM = "AUDITORIUM";
+ private static final String COLUMN_NAME_TEACHER = "TEACHER";
+
+ public ClassDatabaseController(@NotNull Context context) {
+ super(context.getApplicationContext(), DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ Log.d("Database", "onCreate database");
+ db.execSQL("CREATE TABLE " + DATABASE_NAME + " (" +
+ COLUMN_NAME_ID + " INTEGER PRIMARY KEY, " +
+ COLUMN_NAME_SUBJECT + " TEXT, " +
+ COLUMN_NAME_DAY_OF_WEEK + " INTEGER, " +
+ COLUMN_NAME_HOUR + " INTEGER, " +
+ COLUMN_NAME_MINUTE + " INTEGER, " +
+ COLUMN_NAME_WEEK_PARITY + " INTEGER, " +
+ COLUMN_NAME_AUDITORIUM + " TEXT, " +
+ COLUMN_NAME_TEACHER + " TEXT" + ");");
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldDATABASE_VERSION, int newDATABASE_VERSION) {
+ db.execSQL("DROP TABLE IF EXISTS " + DATABASE_NAME);
+ onCreate(db);
+ }
+
+ @NotNull
+ private Class getClassByCursor(@NotNull Cursor cursor) {
+ int id = cursor.getInt(cursor.getColumnIndex(COLUMN_NAME_ID));
+ String subject = cursor.getString(cursor.getColumnIndex(COLUMN_NAME_SUBJECT));
+ int dayOfWeek = cursor.getInt(cursor.getColumnIndex(COLUMN_NAME_DAY_OF_WEEK));
+ int hour = cursor.getInt(cursor.getColumnIndex(COLUMN_NAME_HOUR));
+ int minute = cursor.getInt(cursor.getColumnIndex(COLUMN_NAME_MINUTE));
+ int weekParity = cursor.getInt(cursor.getColumnIndex(COLUMN_NAME_WEEK_PARITY));
+ String auditorium = cursor.getString(cursor.getColumnIndex(COLUMN_NAME_AUDITORIUM));
+ String teacher = cursor.getString(cursor.getColumnIndex(COLUMN_NAME_TEACHER));
+
+ return new Class(subject, dayOfWeek, hour, minute,
+ WeekParityEnum.values()[weekParity], auditorium, teacher, id);
+ }
+
+ public void addClass(@NotNull Class aClass) {
+ try (SQLiteDatabase database = this.getWritableDatabase()) {
+ ContentValues values = new ContentValues();
+ values.put(COLUMN_NAME_ID, aClass.getId());
+ values.put(COLUMN_NAME_SUBJECT, aClass.getSubject());
+ values.put(COLUMN_NAME_DAY_OF_WEEK, aClass.getDayOfWeek());
+ values.put(COLUMN_NAME_HOUR, aClass.getHour());
+ values.put(COLUMN_NAME_MINUTE, aClass.getMinute());
+ values.put(COLUMN_NAME_WEEK_PARITY, aClass.getWeekParity().ordinal());
+ values.put(COLUMN_NAME_AUDITORIUM, aClass.getAuditorium());
+ values.put(COLUMN_NAME_TEACHER, aClass.getTeacher());
+ long rowId = database.insert(DATABASE_NAME, null, values);
+ Log.d("Database", "inserted row number " + rowId);
+ }
+ }
+
+ @NotNull
+ public List getDaySchedule(int dayOfWeek, WeekParityEnum weekParity) {
+ String query = "SELECT * FROM " + DATABASE_NAME + " WHERE " + COLUMN_NAME_DAY_OF_WEEK + "=? " +
+ "AND (" + COLUMN_NAME_WEEK_PARITY + "=? " + " OR " + COLUMN_NAME_WEEK_PARITY + "=?) " +
+ "ORDER BY " + COLUMN_NAME_HOUR + ", " + COLUMN_NAME_MINUTE;
+
+ String[] selectionArgs = new String[]{String.valueOf(dayOfWeek),
+ String.valueOf(weekParity.ordinal()), String.valueOf(WeekParityEnum.ALWAYS.ordinal())};
+ List daySchedule = new ArrayList<>();
+
+ try (SQLiteDatabase database = this.getReadableDatabase();
+ Cursor cursor = database.rawQuery(query, selectionArgs)) {
+ if (cursor != null && cursor.moveToFirst()) {
+ do {
+ daySchedule.add(getClassByCursor(cursor));
+ } while (cursor.moveToNext());
+ }
+ }
+
+ return daySchedule;
+ }
+
+ @NotNull
+ public List getAllClasses() {
+ String query = "SELECT * FROM " + DATABASE_NAME;
+ List classes = new ArrayList<>();
+
+ try (SQLiteDatabase database = this.getReadableDatabase();
+ Cursor cursor = database.rawQuery(query, null)) {
+ if (cursor != null && cursor.moveToFirst()) {
+ do {
+ classes.add(getClassByCursor(cursor));
+ } while (cursor.moveToNext());
+ }
+ }
+
+ return classes;
+ }
+
+ public void deleteClassById(int id) {
+ try (SQLiteDatabase database = this.getWritableDatabase()) {
+ database.delete(DATABASE_NAME, COLUMN_NAME_ID + " = ?",
+ new String[]{String.valueOf(id)});
+ }
+ }
+
+ public Class getClassById(int id) {
+ String query = "SELECT * FROM " + DATABASE_NAME + " WHERE " + COLUMN_NAME_ID + "=?";
+ String[] selectionArgs = new String[]{String.valueOf(id)};
+
+ try (SQLiteDatabase database = this.getReadableDatabase();
+ Cursor cursor = database.rawQuery(query, selectionArgs)) {
+ cursor.moveToFirst();
+ return getClassByCursor(cursor);
+ }
+ }
+
+ public void editClassById(@NotNull String subject, int dayOfWeek, int hour, int minute,
+ @NotNull WeekParityEnum weekParity, @NotNull String auditorium,
+ @NotNull String teacher, int id) {
+ try (SQLiteDatabase database = this.getWritableDatabase()) {
+ ContentValues values = new ContentValues();
+ values.put(COLUMN_NAME_SUBJECT, subject);
+ values.put(COLUMN_NAME_DAY_OF_WEEK, dayOfWeek);
+ values.put(COLUMN_NAME_HOUR, hour);
+ values.put(COLUMN_NAME_MINUTE, minute);
+ values.put(COLUMN_NAME_WEEK_PARITY, weekParity.ordinal());
+ values.put(COLUMN_NAME_AUDITORIUM, auditorium);
+ values.put(COLUMN_NAME_TEACHER, teacher);
+ database.update(DATABASE_NAME, values, COLUMN_NAME_ID + " = ?",
+ new String[]{String.valueOf(id)});
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/Controller.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/Controller.java
new file mode 100644
index 0000000..7d2d53f
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/Controller.java
@@ -0,0 +1,422 @@
+package ru.spbau.group202.notdeadbydeadline.controller;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import org.jetbrains.annotations.NotNull;
+import org.joda.time.LocalDate;
+import org.joda.time.LocalDateTime;
+import org.apache.commons.collections4.ListUtils;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+
+import ru.spbau.group202.notdeadbydeadline.model.CreditFormEnum;
+import ru.spbau.group202.notdeadbydeadline.model.ScheduleEntry;
+import ru.spbau.group202.notdeadbydeadline.model.Homework;
+import ru.spbau.group202.notdeadbydeadline.model.Class;
+import ru.spbau.group202.notdeadbydeadline.model.StudyMaterial;
+import ru.spbau.group202.notdeadbydeadline.model.SubjectCredit;
+import ru.spbau.group202.notdeadbydeadline.model.WeekParityEnum;
+import ru.spbau.group202.notdeadbydeadline.model.Exam;
+import ru.spbau.group202.notdeadbydeadline.model.ExamEnum;
+import ru.spbau.group202.notdeadbydeadline.model.utilities.AsyncTaskResult;
+import ru.spbau.group202.notdeadbydeadline.model.utilities.ModelUtils;
+import ru.spbau.group202.notdeadbydeadline.model.utilities.StudyMaterialsSourceAccessException;
+import ru.spbau.group202.notdeadbydeadline.model.utilities.StudyMaterialsUpdatingException;
+import ru.spbau.group202.notdeadbydeadline.model.utilities.UnrecognizedCreditFormException;
+import ru.spbau.group202.notdeadbydeadline.model.utilities.UrlDownloadingException;
+import ru.spbau.group202.notdeadbydeadline.model.utilities.WebStudyMaterialException;
+
+public class Controller {
+ private static Controller instance;
+ private File appDirectory;
+ private StoredDataController settingsDatabase;
+ private SubjectDatabaseController subjectDatabase;
+ private ExamController examController;
+ private HomeworkController homeworkController;
+ private ScheduleController scheduleController;
+ private StudyMaterialController studyMaterialController;
+ private Set subjectList;
+
+ private Controller(@NotNull Context context) {
+ homeworkController = new HomeworkController(context);
+ subjectDatabase = new SubjectDatabaseController(context);
+ scheduleController = new ScheduleController(context);
+ examController = new ExamController(context);
+ studyMaterialController = new StudyMaterialController(context);
+ settingsDatabase = new StoredDataController(context);
+ subjectList = new HashSet<>();
+ subjectList.addAll(subjectDatabase.getAllSubjects());
+ appDirectory = context.getApplicationContext().getFilesDir();
+ }
+
+ public static Controller getInstance(Context context) {
+ if (instance == null) {
+ instance = new Controller(context.getApplicationContext());
+ }
+ return instance;
+ }
+
+ public ExamController examController() {
+ return examController;
+ }
+
+ public HomeworkController homeworkController() {
+ return homeworkController;
+ }
+
+ public ScheduleController scheduleController() {
+ return scheduleController;
+ }
+
+ public StudyMaterialController studyMaterialController() {
+ return studyMaterialController;
+ }
+
+ @NotNull
+ public List getSubjectList() {
+ return new ArrayList<>(subjectList);
+ }
+
+ public void setWeekPairity(boolean isInversed) {
+ settingsDatabase.saveWeekPairity(isInversed);
+ }
+
+ @NotNull
+ public List calculateProgress(@NotNull String subject) throws UnrecognizedCreditFormException {
+ SubjectCredit subjectCredit = subjectDatabase.getSubjectCredit(subject);
+ return subjectCredit.calculateProgress(homeworkController.homeworkDatabase.getPassedHomeworksBySubject(subject),
+ examController.examDatabase.getExamsBySubject(subject));
+ }
+
+ public void setSubjectCreditForm(@NotNull String subject, @NotNull CreditFormEnum credit) {
+ subjectDatabase.setSubjectCreditForm(subject, credit);
+ }
+
+
+ public class HomeworkController {
+ private HomeworkDatabaseController homeworkDatabase;
+
+ public HomeworkController(Context context) {
+ homeworkDatabase = new HomeworkDatabaseController(context);
+ }
+
+ @NotNull
+ public List> getHomeworksBySubject(@NotNull String subject) {
+ return ModelUtils.map(homeworkDatabase.getHomeworksBySubject(subject), ModelUtils.HW_FIELDS_TO_STRING_LIST);
+ }
+
+ public void addHomework(@NotNull LocalDateTime deadline, @NotNull String subject,
+ int regularity, String description, String howToSend,
+ double expectedScore, @NotNull ArrayList materials) {
+ int id = settingsDatabase.getTotalNumberOfHW();
+ Homework homework = new Homework(deadline, subject, regularity, description,
+ howToSend, expectedScore, id, materials);
+ homeworkDatabase.addHomework(homework);
+ settingsDatabase.saveTotalNumberOfHW(++id);
+
+ if (subjectList.add(subject)) {
+ subjectDatabase.addSubject(subject, CreditFormEnum.NOT_STATED, -1);
+ }
+ }
+
+ public void deleteHomeworkById(int id) {
+ homeworkDatabase.deleteHomeworkById(id);
+ }
+
+ public void setHomeworkScoreById(int id, int score) {
+ homeworkDatabase.setScoreById(id, score);
+ }
+
+ public void setHomeworkDeferralById(int id, int deferral) {
+ homeworkDatabase.setDeferralById(id, deferral);
+ }
+
+ public void editHomeworkById(int id, @NotNull LocalDateTime deadline, @NotNull String subject,
+ int regularity, String description, String howToSend,
+ double expectedScore, @NotNull ArrayList materials) {
+ homeworkDatabase.editHomeworkById(deadline, subject, regularity, description, howToSend,
+ expectedScore, id, materials);
+ if (subjectList.add(subject)) {
+ subjectDatabase.addSubject(subject, CreditFormEnum.NOT_STATED, -1);
+ }
+ }
+
+ @NotNull
+ public Bundle getHomeworkById(int id) {
+ return homeworkDatabase.getHomeworkById(id).getDeconstructed();
+ }
+
+ @NotNull
+ public List> getDeadlinesByDay(@NotNull LocalDate date) {
+ return ModelUtils.map(homeworkDatabase.getHomeworksByDay(date), ModelUtils.HW_DEADLINE_FIELDS_TO_STRING_LIST);
+ }
+
+ public void generateHomeworks() {
+ LocalDate today = LocalDate.now();
+ for (Homework homework : homeworkDatabase.getHomeworksByDay(today)) {
+ if (homework.getRegularity() != 0) {
+ int id = settingsDatabase.getTotalNumberOfHW();
+ homeworkDatabase.addHomework(homework.generateNewHomeworkById(id));
+ settingsDatabase.saveTotalNumberOfHW(++id);
+ }
+ }
+ }
+ }
+
+ public class ScheduleController {
+ private ClassDatabaseController classDatabase;
+
+ public ScheduleController(Context context) {
+ classDatabase = new ClassDatabaseController(context);
+ }
+
+ @NotNull
+ public List> getScheduleByDay(LocalDate day) {
+ WeekParityEnum weekParity = WeekParityEnum.values()[day.getWeekOfWeekyear() % 2];
+ if (settingsDatabase.getParityOfWeek()) {
+ weekParity = weekParity.inverse();
+ }
+
+ List classes = classDatabase.getDaySchedule(day.getDayOfWeek() - 1,
+ weekParity);
+ List exams = examController.examDatabase.getExamsByDay(day);
+ List scheduleEntries = ListUtils.union(exams, classes);
+ Collections.sort(scheduleEntries);
+
+ return ModelUtils.map(scheduleEntries, ModelUtils.SCHEDULE_ENTRY_TO_SCHEDULE_DESCRIPTION);
+ }
+
+ public void addClass(@NotNull String subject, int dayOfWeek, int hour,
+ int minute, @NotNull WeekParityEnum weekParity,
+ @NotNull String auditorium, @NotNull String teacher) {
+ int id = settingsDatabase.getTotalNumberOfScheduleEntries();
+ Class aClass = new Class(subject, dayOfWeek, hour, minute,
+ weekParity, auditorium, teacher, id);
+ classDatabase.addClass(aClass);
+ settingsDatabase.saveTotalNumberOfScheduleEntries(++id);
+ }
+
+ public void deleteClassById(int id) {
+ classDatabase.deleteClassById(id);
+ }
+
+ public void editClassById(int id, @NotNull String subject, int dayOfWeek,
+ int hour, int minute, @NotNull WeekParityEnum weekParity,
+ @NotNull String auditorium, @NotNull String teacher) {
+ classDatabase.editClassById(subject, dayOfWeek, hour, minute,
+ weekParity, auditorium, teacher, id);
+ }
+
+ @NotNull
+ public Bundle getClassById(int id) {
+ return classDatabase.getClassById(id).getDeconstructed();
+ }
+ }
+
+ public class ExamController {
+ private ExamDatabaseController examDatabase;
+
+ public ExamController(Context context) {
+ examDatabase = new ExamDatabaseController(context);
+ }
+
+ @NotNull
+ public List> getExamsBySubject(@NotNull String subject) {
+ return ModelUtils.map(examDatabase.getExamsBySubject(subject), ModelUtils.EXAM_FIELDS_TO_STRING_LIST);
+ }
+
+ @NotNull
+ public List> getExamsByDay(@NotNull LocalDate date) {
+ return ModelUtils.map(examDatabase.getExamsByDay(date), ModelUtils.EXAM_FIELDS_TO_STRING_LIST);
+ }
+
+ public void addExam(@NotNull LocalDateTime date, @NotNull String subject,
+ @NotNull ExamEnum examEnum, String description) {
+ int id = settingsDatabase.getTotalNumberOfWorks();
+ Exam exam = new Exam(subject, description, date, examEnum, id);
+ examDatabase.addExam(exam);
+ settingsDatabase.saveTotalNumberOfWorks(++id);
+
+ if (subjectList.add(subject)) {
+ subjectDatabase.addSubject(subject, CreditFormEnum.NOT_STATED, -1);
+ }
+ }
+
+ public void deleteExamsById(int id) {
+ examDatabase.deleteExamById(id);
+ }
+
+ public void setExamAcceptedById(int id, boolean isAccepted) {
+ examDatabase.setAcceptedById(id, isAccepted);
+ }
+
+ public void editExamById(int id, @NotNull LocalDateTime date, @NotNull String subject,
+ @NotNull ExamEnum examEnum, String description) {
+ examDatabase.editExamById(subject, description, date, examEnum, id);
+ if (subjectList.add(subject)) {
+ subjectDatabase.addSubject(subject, CreditFormEnum.NOT_STATED, -1);
+ }
+ }
+
+ @NotNull
+ public Bundle getExamById(int id) {
+ return examDatabase.getExamById(id).getDeconstructed();
+ }
+ }
+
+ public class StudyMaterialController {
+ private StudyMaterialDatabaseController studyMaterialDatabase;
+
+ public StudyMaterialController(Context context) {
+ studyMaterialDatabase = new StudyMaterialDatabaseController(context);
+ }
+
+ public void addUpdatableStudyMaterial(@NotNull String name, @NotNull String subject, int term)
+ throws WebStudyMaterialException, StudyMaterialsSourceAccessException {
+ int id = settingsDatabase.getTotalNumberOfStudyMaterials();
+ StudyMaterial studyMaterial = new StudyMaterial(name, subject, term,
+ appDirectory.getAbsolutePath(), 0, id);
+ File studyMaterialFile = new File(appDirectory.getAbsolutePath(), name);
+
+ if (studyMaterialFile.exists()) {
+ throw new WebStudyMaterialException("The study material already exists.",
+ ModelUtils.STUDY_MATERIAL_FIELDS_TO_STRING_LIST.apply(studyMaterial));
+ }
+ updateStudyMaterial(studyMaterial);
+ studyMaterialDatabase.addStudyMaterial(studyMaterial);
+ settingsDatabase.saveTotalNumberOfStudyMaterials(++id);
+
+ if (!subjectList.add(subject)) {
+ subjectDatabase.addSubject(subject, CreditFormEnum.NOT_STATED, -1);
+ }
+ }
+
+ public void addLocalStudyMaterial(@NotNull String name, @NotNull String subject,
+ int term, @NotNull String path) {
+ int id = settingsDatabase.getTotalNumberOfStudyMaterials();
+ StudyMaterial studyMaterial = new StudyMaterial(name, subject, term, path, -1, id);
+ studyMaterialDatabase.addStudyMaterial(studyMaterial);
+ settingsDatabase.saveTotalNumberOfStudyMaterials(++id);
+
+ if (!subjectList.add(subject)) {
+ subjectDatabase.addSubject(subject, CreditFormEnum.NOT_STATED, -1);
+ }
+ }
+
+ @NotNull
+ public List> getStudyMaterialsBySubject(@NotNull String subject) {
+ return ModelUtils.map(studyMaterialDatabase.getStudyMaterialsBySubject(subject),
+ ModelUtils.STUDY_MATERIAL_FIELDS_TO_STRING_LIST);
+ }
+
+ @NotNull
+ public List> getStudyMaterialsByTerm(int term) {
+ return ModelUtils.map(studyMaterialDatabase.getStudyMaterialsByTerm(term),
+ ModelUtils.STUDY_MATERIAL_FIELDS_TO_STRING_LIST);
+ }
+
+ @NotNull
+ public Bundle getStudyMaterialById(int id) {
+ return studyMaterialDatabase.getStudyMaterialById(id).getDeconstructed();
+ }
+
+ public void deleteStudyMaterialById(int id) {
+ studyMaterialDatabase.deleteStudyMaterialById(id);
+ }
+
+ public void editStudyMaterialById(int id, @NotNull String subject, int term) {
+ studyMaterialDatabase.editStudyMaterialById(id, subject, term);
+ if (!subjectList.add(subject)) {
+ subjectDatabase.addSubject(subject, CreditFormEnum.NOT_STATED, -1);
+ }
+ }
+
+ public void updateStudyMaterials() throws StudyMaterialsUpdatingException, UrlDownloadingException {
+ StudyMaterialsUpdater studyMaterialsUpdater;
+ StudyMaterial[] studyMaterials = studyMaterialDatabase.getUpdatableStudyMaterials()
+ .toArray(new StudyMaterial[0]);
+
+ try {
+ studyMaterialsUpdater = new StudyMaterialsUpdater();
+ studyMaterialsUpdater.execute(studyMaterials);
+ studyMaterialsUpdater.get(1, TimeUnit.MINUTES);
+ } catch (Exception exception) {
+ throw new UrlDownloadingException();
+ }
+
+ StudyMaterialsUpdatingException exception = studyMaterialsUpdater.getError();
+ if (exception != null) {
+ throw exception;
+ }
+ }
+
+ @NotNull
+ public List[] getAvailableStudyMaterialsList() throws StudyMaterialsSourceAccessException {
+ WebStudyMaterialsListGetter webStudyMaterialsListGetter = new WebStudyMaterialsListGetter();
+ webStudyMaterialsListGetter.execute();
+ AsyncTaskResult[], StudyMaterialsSourceAccessException> result;
+
+ try {
+ result = webStudyMaterialsListGetter.get(1, TimeUnit.MINUTES);
+ } catch (Exception exception) {
+ throw new StudyMaterialsSourceAccessException();
+ }
+
+ StudyMaterialsSourceAccessException exception = result.getError();
+ if (exception == null) {
+ return result.getResult();
+ } else {
+ throw exception;
+ }
+ }
+
+ @NotNull
+ public List searchForStudyMaterials(String query) throws UrlDownloadingException {
+ GoogleSearchRequester googleSearchRequester = new GoogleSearchRequester();
+ googleSearchRequester.execute(query, appDirectory.getAbsolutePath());
+ AsyncTaskResult, UrlDownloadingException> result;
+
+ try {
+ result = googleSearchRequester.get(1, TimeUnit.MINUTES);
+ } catch (Exception exception) {
+ throw new UrlDownloadingException();
+ }
+
+ UrlDownloadingException exception = result.getError();
+ if (exception == null) {
+ return result.getResult();
+ } else {
+ throw exception;
+ }
+
+ }
+
+ private void updateStudyMaterial(StudyMaterial studyMaterial)
+ throws StudyMaterialsSourceAccessException, WebStudyMaterialException {
+ StudyMaterialsUpdater studyMaterialsUpdater;
+ try {
+ studyMaterialsUpdater = new StudyMaterialsUpdater();
+ studyMaterialsUpdater.execute(studyMaterial);
+ studyMaterialsUpdater.get(1, TimeUnit.MINUTES);
+ } catch (Exception exception) {
+ throw new WebStudyMaterialException("Unable to download study material.",
+ ModelUtils.STUDY_MATERIAL_FIELDS_TO_STRING_LIST.apply(studyMaterial));
+ }
+
+ StudyMaterialsUpdatingException exception = studyMaterialsUpdater.getError();
+ if (exception != null) {
+ throw exception.getErrors().get(0);
+ }
+ }
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/ExamDatabaseController.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/ExamDatabaseController.java
new file mode 100644
index 0000000..e514621
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/ExamDatabaseController.java
@@ -0,0 +1,202 @@
+package ru.spbau.group202.notdeadbydeadline.controller;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
+
+import org.jetbrains.annotations.NotNull;
+import org.joda.time.LocalDate;
+import org.joda.time.LocalDateTime;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import ru.spbau.group202.notdeadbydeadline.model.Exam;
+import ru.spbau.group202.notdeadbydeadline.model.ExamEnum;
+
+
+public class ExamDatabaseController extends SQLiteOpenHelper {
+ private static final String DATABASE_NAME = "Exams";
+ private static final int DATABASE_VERSION = 1;
+ private static final String COLUMN_NAME_ID = "ID";
+ private static final String COLUMN_NAME_SUBJECT = "SUBJECT";
+ private static final String COLUMN_NAME_YEAR = "YEAR";
+ private static final String COLUMN_NAME_MONTH = "MONTH";
+ private static final String COLUMN_NAME_DAY = "DAY";
+ private static final String COLUMN_NAME_HOUR = "HOUR";
+ private static final String COLUMN_NAME_MINUTE = "MINUTE";
+ private static final String COLUMN_NAME_DESCRIPTION = "DESCRIPTION";
+ private static final String COLUMN_NAME_EXAM_TYPE = "EXAM_TYPE";
+ private static final String COLUMN_NAME_IS_ACCEPTED = "IS_ACCEPTED";
+
+ public ExamDatabaseController(@NotNull Context context) {
+ super(context.getApplicationContext(), DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ Log.d("Database", "onCreate database");
+ db.execSQL("CREATE TABLE " + DATABASE_NAME + " (" +
+ COLUMN_NAME_ID + " INTEGER PRIMARY KEY, " +
+ COLUMN_NAME_SUBJECT + " TEXT, " +
+ COLUMN_NAME_YEAR + " INTEGER, " +
+ COLUMN_NAME_MONTH + " INTEGER, " +
+ COLUMN_NAME_DAY + " INTEGER, " +
+ COLUMN_NAME_HOUR + " INTEGER, " +
+ COLUMN_NAME_MINUTE + " INTEGER, " +
+ COLUMN_NAME_DESCRIPTION + " TEXT, " +
+ COLUMN_NAME_EXAM_TYPE + " INTEGER, " +
+ COLUMN_NAME_IS_ACCEPTED + " INTEGER" + ");");
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldDATABASE_VERSION, int newDATABASE_VERSION) {
+ db.execSQL("DROP TABLE IF EXISTS " + DATABASE_NAME);
+ onCreate(db);
+ }
+
+ @NotNull
+ private Exam getExamByCursor(@NotNull Cursor cursor) {
+ int id = cursor.getInt(cursor.getColumnIndex(COLUMN_NAME_ID));
+ String subject = cursor.getString(cursor.getColumnIndex(COLUMN_NAME_SUBJECT));
+ int year = cursor.getInt(cursor.getColumnIndex(COLUMN_NAME_YEAR));
+ int month = cursor.getInt(cursor.getColumnIndex(COLUMN_NAME_MONTH));
+ int day = cursor.getInt(cursor.getColumnIndex(COLUMN_NAME_DAY));
+ int hour = cursor.getInt(cursor.getColumnIndex(COLUMN_NAME_HOUR));
+ int minute = cursor.getInt(cursor.getColumnIndex(COLUMN_NAME_MINUTE));
+ String description = cursor.getString(cursor.getColumnIndex(COLUMN_NAME_DESCRIPTION));
+ int examType = cursor.getInt(cursor.getColumnIndex(COLUMN_NAME_EXAM_TYPE));
+ boolean isAccepted = cursor.getInt(cursor.getColumnIndex(COLUMN_NAME_IS_ACCEPTED)) == 1;
+
+ LocalDateTime date = new LocalDateTime(year, month, day, hour, minute);
+ Exam exam = new Exam(subject, description, date, ExamEnum.values()[examType], id);
+ exam.setAccepted(isAccepted);
+ return exam;
+ }
+
+ public void addExam(@NotNull Exam exam) {
+ try (SQLiteDatabase database = this.getWritableDatabase()) {
+ ContentValues values = new ContentValues();
+ values.put(COLUMN_NAME_ID, exam.getId());
+ values.put(COLUMN_NAME_SUBJECT, exam.getSubject());
+ values.put(COLUMN_NAME_YEAR, exam.getYear());
+ values.put(COLUMN_NAME_MONTH, exam.getMonth());
+ values.put(COLUMN_NAME_DAY, exam.getDay());
+ values.put(COLUMN_NAME_HOUR, exam.getHour());
+ values.put(COLUMN_NAME_MINUTE, exam.getMinute());
+ values.put(COLUMN_NAME_DESCRIPTION, exam.getDescription());
+ values.put(COLUMN_NAME_EXAM_TYPE, exam.getExamType().ordinal());
+ values.put(COLUMN_NAME_IS_ACCEPTED, exam.isAccepted() ? 1 : 0);
+ long rowId = database.insert(DATABASE_NAME, null, values);
+ Log.d("Database", "inserted row number " + rowId);
+ }
+ }
+
+ @NotNull
+ public List getExamsBySubject(@NotNull String subject) {
+ String query = "SELECT * FROM " + DATABASE_NAME +
+ " WHERE " + COLUMN_NAME_SUBJECT + "=" + "?";
+ List exams = new ArrayList<>();
+ String[] selectionArgs = new String[]{String.valueOf(subject)};
+
+ try (SQLiteDatabase database = this.getReadableDatabase();
+ Cursor cursor = database.rawQuery(query, selectionArgs)) {
+ if (cursor != null && cursor.moveToFirst()) {
+ do {
+ exams.add(getExamByCursor(cursor));
+ } while (cursor.moveToNext());
+ }
+ }
+
+ Collections.sort(exams);
+ return exams;
+ }
+
+ @NotNull
+ public List getAllExams() {
+ String query = "SELECT * FROM " + DATABASE_NAME;
+ List exams = new ArrayList<>();
+
+ try (SQLiteDatabase database = this.getReadableDatabase();
+ Cursor cursor = database.rawQuery(query, null)) {
+ if (cursor != null && cursor.moveToFirst()) {
+ do {
+ exams.add(getExamByCursor(cursor));
+ } while (cursor.moveToNext());
+ }
+ }
+
+ return exams;
+ }
+
+ @NotNull
+ public List getExamsByDay(LocalDate date) {
+ String query = "SELECT * FROM " + DATABASE_NAME + " WHERE " + COLUMN_NAME_YEAR + "=? " +
+ "AND " + COLUMN_NAME_MONTH + "=? " + "AND " + COLUMN_NAME_DAY + "=?" +
+ "ORDER BY " + COLUMN_NAME_HOUR + ", " + COLUMN_NAME_MINUTE + " ASC";
+ String[] selectionArgs = new String[]{String.valueOf(date.getYear()),
+ String.valueOf(date.getMonthOfYear()), String.valueOf(date.getDayOfMonth())};
+ List exams = new ArrayList<>();
+
+ try (SQLiteDatabase database = this.getReadableDatabase();
+ Cursor cursor = database.rawQuery(query, selectionArgs)) {
+ if (cursor != null && cursor.moveToFirst()) {
+ do {
+ Exam exam = getExamByCursor(cursor);
+ exams.add(exam);
+ } while (cursor.moveToNext());
+ }
+ }
+
+ return exams;
+ }
+
+ public void deleteExamById(int id) {
+ try (SQLiteDatabase database = this.getWritableDatabase()) {
+ database.delete(DATABASE_NAME, COLUMN_NAME_ID + " = ?",
+ new String[]{String.valueOf(id)});
+ }
+ }
+
+ public void setAcceptedById(int id, boolean isAccepted) {
+ try (SQLiteDatabase database = this.getWritableDatabase()) {
+ ContentValues values = new ContentValues();
+ values.put(COLUMN_NAME_IS_ACCEPTED, isAccepted);
+ database.update(DATABASE_NAME, values, COLUMN_NAME_ID + " = ?",
+ new String[]{String.valueOf(id)});
+ }
+ }
+
+ @NotNull
+ public Exam getExamById(int id) {
+ String query = "SELECT * FROM " + DATABASE_NAME + " WHERE " + COLUMN_NAME_ID + "=?";
+ String[] selectionArgs = new String[]{String.valueOf(id)};
+
+ try (SQLiteDatabase database = this.getReadableDatabase();
+ Cursor cursor = database.rawQuery(query, selectionArgs)) {
+ cursor.moveToFirst();
+ return getExamByCursor(cursor);
+ }
+ }
+
+ public void editExamById(@NotNull String subject, @NotNull String description,
+ @NotNull LocalDateTime date, @NotNull ExamEnum examEnum, int id) {
+ try (SQLiteDatabase database = this.getWritableDatabase()) {
+ ContentValues values = new ContentValues();
+ values.put(COLUMN_NAME_SUBJECT, subject);
+ values.put(COLUMN_NAME_YEAR, date.getYear());
+ values.put(COLUMN_NAME_MONTH, date.getMonthOfYear());
+ values.put(COLUMN_NAME_DAY, date.getDayOfMonth());
+ values.put(COLUMN_NAME_HOUR, date.getHourOfDay());
+ values.put(COLUMN_NAME_MINUTE, date.getMinuteOfHour());
+ values.put(COLUMN_NAME_DESCRIPTION, description);
+ values.put(COLUMN_NAME_EXAM_TYPE, examEnum.ordinal());
+ database.update(DATABASE_NAME, values, COLUMN_NAME_ID + " = ?",
+ new String[]{String.valueOf(id)});
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/GoogleSearchRequester.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/GoogleSearchRequester.java
new file mode 100644
index 0000000..760ccc4
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/GoogleSearchRequester.java
@@ -0,0 +1,53 @@
+package ru.spbau.group202.notdeadbydeadline.controller;
+
+
+import android.os.AsyncTask;
+
+import org.apache.commons.io.FileUtils;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.jsoup.select.Elements;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+
+import ru.spbau.group202.notdeadbydeadline.model.utilities.AsyncTaskResult;
+import ru.spbau.group202.notdeadbydeadline.model.utilities.UrlDownloadingException;
+
+public class GoogleSearchRequester
+ extends AsyncTask, UrlDownloadingException>> {
+ private static final String GOOGLE_SEARCH_URL = "https://www.google.com/search";
+ private static final int NUMBER_OF_SEARCH_TRIES = 5;
+
+ @Override
+ protected AsyncTaskResult, UrlDownloadingException> doInBackground(String... strings) {
+ ArrayList results = new ArrayList<>();
+ String query = strings[0];
+ String appDirectory = strings[1];
+ String searchURL = GOOGLE_SEARCH_URL + "?q=" + "filetype:pdf " + "+"
+ + query.replace(" ", "+") + "&num="
+ + Integer.toString(NUMBER_OF_SEARCH_TRIES);
+
+ try {
+ Document html = Jsoup.connect(searchURL).userAgent("Mozilla").timeout(5000).get();
+ Elements links = html.select("h3.r > a");
+ for (Element link : links) {
+ String stringUrl = link.attr("href").replace("/url?q=", "").replaceAll("\\.pdf.*", ".pdf");
+ URL url = new URL(stringUrl);
+ File searchResultsFolder = new File(appDirectory + File.separator + "search");
+ searchResultsFolder.mkdir();
+ File foundMaterial = new File(searchResultsFolder.getAbsolutePath(), link.text());
+ FileUtils.copyURLToFile(url, foundMaterial);
+ results.add(foundMaterial);
+ }
+ } catch (IOException exception) {
+ return new AsyncTaskResult<>(new UrlDownloadingException());
+ }
+
+ return new AsyncTaskResult<>(results);
+ }
+}
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/HomeworkDatabaseController.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/HomeworkDatabaseController.java
new file mode 100644
index 0000000..cbe7057
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/HomeworkDatabaseController.java
@@ -0,0 +1,251 @@
+package ru.spbau.group202.notdeadbydeadline.controller;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
+
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+
+import org.jetbrains.annotations.NotNull;
+import org.joda.time.LocalDate;
+import org.joda.time.LocalDateTime;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import ru.spbau.group202.notdeadbydeadline.model.Homework;
+
+public class HomeworkDatabaseController extends SQLiteOpenHelper {
+ private static final String DATABASE_NAME = "Homeworks";
+ private static final int DATABASE_VERSION = 1;
+ private static final String COLUMN_NAME_ID = "ID";
+ private static final String COLUMN_NAME_SUBJECT = "SUBJECT";
+ private static final String COLUMN_NAME_YEAR = "YEAR";
+ private static final String COLUMN_NAME_MONTH = "MONTH";
+ private static final String COLUMN_NAME_DAY = "DAY";
+ private static final String COLUMN_NAME_HOUR = "HOUR";
+ private static final String COLUMN_NAME_MINUTE = "MINUTE";
+ private static final String COLUMN_NAME_REGULARITY = "REGULARITY";
+ private static final String COLUMN_NAME_EXPECTED_SCORE = "EXPECTED_SCORE";
+ private static final String COLUMN_NAME_ACTUAL_SCORE = "ACTUAL_SCORE";
+ private static final String COLUMN_NAME_DESCRIPTION = "DESCRIPTION";
+ private static final String COLUMN_NAME_HOW_TO_SEND = "HOW_TO_SEND";
+ private static final String COLUMN_NAME_DEFERRAL = "DEFERRAL";
+ private static final String COLUMN_NAME_MATERIALS = "MATERIALS";
+
+ public HomeworkDatabaseController(@NotNull Context context) {
+ super(context.getApplicationContext(), DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ Log.d("Database", "onCreate database");
+ db.execSQL("CREATE TABLE " + DATABASE_NAME + " (" +
+ COLUMN_NAME_ID + " INTEGER PRIMARY KEY, " +
+ COLUMN_NAME_SUBJECT + " TEXT, " +
+ COLUMN_NAME_YEAR + " INTEGER, " +
+ COLUMN_NAME_MONTH + " INTEGER, " +
+ COLUMN_NAME_DAY + " INTEGER, " +
+ COLUMN_NAME_HOUR + " INTEGER, " +
+ COLUMN_NAME_MINUTE + " INTEGER, " +
+ COLUMN_NAME_REGULARITY + " INTEGER, " +
+ COLUMN_NAME_EXPECTED_SCORE + " REAL, " +
+ COLUMN_NAME_ACTUAL_SCORE + " INTEGER, " +
+ COLUMN_NAME_DESCRIPTION + " TEXT, " +
+ COLUMN_NAME_HOW_TO_SEND + " TEXT, " +
+ COLUMN_NAME_DEFERRAL + " INTEGER, " +
+ COLUMN_NAME_MATERIALS + " TEXT" + ");");
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldDATABASE_VERSION, int newDATABASE_VERSION) {
+ db.execSQL("DROP TABLE IF EXISTS " + DATABASE_NAME);
+ onCreate(db);
+ }
+
+ @NotNull
+ private Homework getHomeworkByCursor(@NotNull Cursor cursor) {
+ int id = cursor.getInt(cursor.getColumnIndex(COLUMN_NAME_ID));
+ String subject = cursor.getString(cursor.getColumnIndex(COLUMN_NAME_SUBJECT));
+ int year = cursor.getInt(cursor.getColumnIndex(COLUMN_NAME_YEAR));
+ int month = cursor.getInt(cursor.getColumnIndex(COLUMN_NAME_MONTH));
+ int day = cursor.getInt(cursor.getColumnIndex(COLUMN_NAME_DAY));
+ int hour = cursor.getInt(cursor.getColumnIndex(COLUMN_NAME_HOUR));
+ int minute = cursor.getInt(cursor.getColumnIndex(COLUMN_NAME_MINUTE));
+ int regularity = cursor.getInt(cursor.getColumnIndex(COLUMN_NAME_REGULARITY));
+ double expectedScore = cursor.getDouble(cursor.getColumnIndex(COLUMN_NAME_EXPECTED_SCORE));
+ int actualScore = cursor.getInt(cursor.getColumnIndex(COLUMN_NAME_ACTUAL_SCORE));
+ String description = cursor.getString(cursor.getColumnIndex(COLUMN_NAME_DESCRIPTION));
+ String howToSend = cursor.getString(cursor.getColumnIndex(COLUMN_NAME_HOW_TO_SEND));
+ int deferral = cursor.getInt(cursor.getColumnIndex(COLUMN_NAME_DEFERRAL));
+ String gsonMaterials = cursor.getString(cursor.getColumnIndex(COLUMN_NAME_MATERIALS));
+ ArrayList materials = new Gson().fromJson(gsonMaterials,
+ new TypeToken>() {
+ }.getType());
+
+
+ LocalDateTime deadline = new LocalDateTime(year, month, day, hour, minute);
+ Homework homework = new Homework(deadline, subject, regularity, description, howToSend,
+ expectedScore, id, materials);
+ homework.setActualScore(actualScore);
+ homework.assignDeferral(deferral);
+ return homework;
+ }
+
+ public void addHomework(@NotNull Homework homework) {
+ try (SQLiteDatabase database = this.getWritableDatabase()) {
+ ContentValues values = new ContentValues();
+ values.put(COLUMN_NAME_ID, homework.getId());
+ values.put(COLUMN_NAME_SUBJECT, homework.getSubject());
+ values.put(COLUMN_NAME_YEAR, homework.getYear());
+ values.put(COLUMN_NAME_MONTH, homework.getMonth());
+ values.put(COLUMN_NAME_DAY, homework.getDay());
+ values.put(COLUMN_NAME_HOUR, homework.getHour());
+ values.put(COLUMN_NAME_MINUTE, homework.getMinute());
+ values.put(COLUMN_NAME_REGULARITY, homework.getRegularity());
+ values.put(COLUMN_NAME_EXPECTED_SCORE, homework.getExpectedScore());
+ values.put(COLUMN_NAME_ACTUAL_SCORE, homework.getActualScore());
+ values.put(COLUMN_NAME_DESCRIPTION, homework.getDescription());
+ values.put(COLUMN_NAME_HOW_TO_SEND, homework.getHowToSend());
+ values.put(COLUMN_NAME_DEFERRAL, homework.getDeferral());
+ values.put(COLUMN_NAME_MATERIALS, new Gson().toJson(homework.getMaterials()));
+ long rowId = database.insert(DATABASE_NAME, null, values);
+ Log.d("Database", "inserted row number " + rowId);
+ }
+ }
+
+ @NotNull
+ public List getHomeworksBySubject(@NotNull String subject) {
+ String query = "SELECT * FROM " + DATABASE_NAME +
+ " WHERE " + COLUMN_NAME_SUBJECT + "=" + "?";
+ List homeworks = new ArrayList<>();
+ String[] selectionArgs = new String[]{String.valueOf(subject)};
+
+ try (SQLiteDatabase database = this.getReadableDatabase();
+ Cursor cursor = database.rawQuery(query, selectionArgs)) {
+ if (cursor != null && cursor.moveToFirst()) {
+ do {
+ homeworks.add(getHomeworkByCursor(cursor));
+ } while (cursor.moveToNext());
+ }
+ }
+
+ Collections.sort(homeworks, Homework::compareTo);
+ return homeworks;
+ }
+
+ @NotNull
+ public List getActualHomeworks() {
+ String query = "SELECT * FROM " + DATABASE_NAME;
+ List homeworks = new ArrayList<>();
+
+ try (SQLiteDatabase database = this.getReadableDatabase();
+ Cursor cursor = database.rawQuery(query, null)) {
+ if (cursor != null && cursor.moveToFirst()) {
+ do {
+ Homework homework = getHomeworkByCursor(cursor);
+ if(!homework.hasPassed()) {
+ homeworks.add(homework);
+ }
+ } while (cursor.moveToNext());
+ }
+ }
+
+ return homeworks;
+ }
+
+ @NotNull
+ public List getPassedHomeworksBySubject(@NotNull String subject) {
+ List passedHomeworks = new ArrayList<>();
+ for (Homework homework : getHomeworksBySubject(subject)) {
+ if (homework.hasPassed()) {
+ passedHomeworks.add(homework);
+ }
+ }
+
+ return passedHomeworks;
+ }
+
+ @NotNull
+ public List getHomeworksByDay(LocalDate date) {
+ String query = "SELECT * FROM " + DATABASE_NAME + " WHERE " + COLUMN_NAME_YEAR + "=? " +
+ "AND " + COLUMN_NAME_MONTH + "=? " + "AND " + COLUMN_NAME_DAY + "=?" +
+ "ORDER BY " + COLUMN_NAME_HOUR + ", " + COLUMN_NAME_MINUTE + " ASC";
+ String[] selectionArgs = new String[]{String.valueOf(date.getYear()),
+ String.valueOf(date.getMonthOfYear()), String.valueOf(date.getDayOfMonth())};
+ List homeworks = new ArrayList<>();
+
+ try (SQLiteDatabase database = this.getReadableDatabase();
+ Cursor cursor = database.rawQuery(query, selectionArgs)) {
+ if (cursor != null && cursor.moveToFirst()) {
+ do {
+ Homework homework = getHomeworkByCursor(cursor);
+ homeworks.add(homework);
+ } while (cursor.moveToNext());
+ }
+ }
+
+ return homeworks;
+ }
+
+ public void deleteHomeworkById(int id) {
+ try (SQLiteDatabase database = this.getWritableDatabase()) {
+ database.delete(DATABASE_NAME, COLUMN_NAME_ID + " = ?",
+ new String[]{String.valueOf(id)});
+ }
+ }
+
+ public void setScoreById(int id, int score) {
+ try (SQLiteDatabase database = this.getWritableDatabase()) {
+ ContentValues values = new ContentValues();
+ values.put(COLUMN_NAME_ACTUAL_SCORE, score);
+ database.update(DATABASE_NAME, values, COLUMN_NAME_ID + " = ?",
+ new String[]{String.valueOf(id)});
+ }
+ }
+
+ public void setDeferralById(int id, int deferral) {
+ Homework homework = getHomeworkById(id);
+ deleteHomeworkById(id);
+ homework.setDeferral(deferral);
+ addHomework(homework);
+ }
+
+ @NotNull
+ public Homework getHomeworkById(int id) {
+ String query = "SELECT * FROM " + DATABASE_NAME + " WHERE " + COLUMN_NAME_ID + "=?";
+ String[] selectionArgs = new String[]{String.valueOf(id)};
+
+ try (SQLiteDatabase database = this.getReadableDatabase();
+ Cursor cursor = database.rawQuery(query, selectionArgs)) {
+ cursor.moveToFirst();
+ return getHomeworkByCursor(cursor);
+ }
+ }
+
+ public void editHomeworkById(LocalDateTime deadline, @NotNull String subject, int regularity,
+ @NotNull String description, @NotNull String howToSend,
+ double expectedScore, int id, @NotNull ArrayList materials) {
+ try (SQLiteDatabase database = this.getWritableDatabase()) {
+ ContentValues values = new ContentValues();
+ values.put(COLUMN_NAME_SUBJECT, subject);
+ values.put(COLUMN_NAME_YEAR, deadline.getYear());
+ values.put(COLUMN_NAME_MONTH, deadline.getMonthOfYear());
+ values.put(COLUMN_NAME_DAY, deadline.getDayOfMonth());
+ values.put(COLUMN_NAME_HOUR, deadline.getHourOfDay());
+ values.put(COLUMN_NAME_MINUTE, deadline.getMinuteOfHour());
+ values.put(COLUMN_NAME_REGULARITY, regularity);
+ values.put(COLUMN_NAME_EXPECTED_SCORE, expectedScore);
+ values.put(COLUMN_NAME_DESCRIPTION, description);
+ values.put(COLUMN_NAME_HOW_TO_SEND, howToSend);
+ values.put(COLUMN_NAME_MATERIALS, new Gson().toJson(materials));
+ database.update(DATABASE_NAME, values, COLUMN_NAME_ID + " = ?",
+ new String[]{String.valueOf(id)});
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/StoredDataController.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/StoredDataController.java
new file mode 100644
index 0000000..05c8466
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/StoredDataController.java
@@ -0,0 +1,74 @@
+package ru.spbau.group202.notdeadbydeadline.controller;
+
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+import org.jetbrains.annotations.NotNull;
+
+public class StoredDataController {
+ private static final String APP_PREFERENCES = "Settings";
+ private static final String APP_PREFERENCES_INVERSE_WEEK_PARITY = "INVERSE_WEEK_PARITY";
+ private static final String APP_PREFERENCES_TOTAL_NUMBER_OF_HW = "TOTAL_NUMBER_OF_HW";
+ private static final String APP_PREFERENCES_TOTAL_NUMBER_OF_SCHEDULE_ENTRIES
+ = "TOTAL_NUMBER_OF_SCHEDULE_ENTRIES";
+ private static final String APP_PREFERENCES_TOTAL_NUMBER_OF_EXAMS = "TOTAL_NUMBER_OF_EXAMS";
+ private static final String APP_PREFERENCES_TOTAL_NUMBER_OF_STUDY_MATERIALS
+ = "TOTAL_NUMBER_OF_STUDY_MATERIALS";
+ private SharedPreferences settings;
+
+ public StoredDataController(@NotNull Context context) {
+ settings = context.getApplicationContext()
+ .getSharedPreferences(APP_PREFERENCES, Context.MODE_PRIVATE);
+ }
+
+ public void saveWeekPairity(boolean isInversed) {
+ SharedPreferences.Editor editor = settings.edit();
+ editor.putBoolean(APP_PREFERENCES_INVERSE_WEEK_PARITY, isInversed);
+ editor.apply();
+ }
+
+ public void saveTotalNumberOfHW(int totalNumberOfHW) {
+ SharedPreferences.Editor editor = settings.edit();
+ editor.putInt(APP_PREFERENCES_TOTAL_NUMBER_OF_HW, totalNumberOfHW);
+ editor.apply();
+ }
+
+ public void saveTotalNumberOfScheduleEntries(int totalNumberOfScheduleEntries) {
+ SharedPreferences.Editor editor = settings.edit();
+ editor.putInt(APP_PREFERENCES_TOTAL_NUMBER_OF_SCHEDULE_ENTRIES, totalNumberOfScheduleEntries);
+ editor.apply();
+ }
+
+ public void saveTotalNumberOfWorks(int totalNumberOfWorks) {
+ SharedPreferences.Editor editor = settings.edit();
+ editor.putInt(APP_PREFERENCES_TOTAL_NUMBER_OF_EXAMS, totalNumberOfWorks);
+ editor.apply();
+ }
+
+ public void saveTotalNumberOfStudyMaterials(int totalNumberOfStudyMaterials) {
+ SharedPreferences.Editor editor = settings.edit();
+ editor.putInt(APP_PREFERENCES_TOTAL_NUMBER_OF_STUDY_MATERIALS, totalNumberOfStudyMaterials);
+ editor.apply();
+ }
+
+ public boolean getParityOfWeek() {
+ return settings.getBoolean(APP_PREFERENCES_INVERSE_WEEK_PARITY, false);
+ }
+
+ public int getTotalNumberOfHW() {
+ return settings.getInt(APP_PREFERENCES_TOTAL_NUMBER_OF_HW, 0);
+ }
+
+ public int getTotalNumberOfScheduleEntries() {
+ return settings.getInt(APP_PREFERENCES_TOTAL_NUMBER_OF_SCHEDULE_ENTRIES, 0);
+ }
+
+ public int getTotalNumberOfWorks() {
+ return settings.getInt(APP_PREFERENCES_TOTAL_NUMBER_OF_EXAMS, 0);
+ }
+
+ public int getTotalNumberOfStudyMaterials() {
+ return settings.getInt(APP_PREFERENCES_TOTAL_NUMBER_OF_STUDY_MATERIALS, 0);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/StudyMaterialDatabaseController.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/StudyMaterialDatabaseController.java
new file mode 100644
index 0000000..230b320
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/StudyMaterialDatabaseController.java
@@ -0,0 +1,160 @@
+package ru.spbau.group202.notdeadbydeadline.controller;
+
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import ru.spbau.group202.notdeadbydeadline.model.StudyMaterial;
+
+public class StudyMaterialDatabaseController extends SQLiteOpenHelper {
+ private static final String DATABASE_NAME = "StudyMaterials";
+ private static final int DATABASE_VERSION = 1;
+ private static final String COLUMN_NAME_ID = "ID";
+ private static final String COLUMN_NAME_SUBJECT = "SUBJECT";
+ private static final String COLUMN_NAME_TERM = "TERM";
+ private static final String COLUMN_NAME_VERSION = "VERSION";
+ private static final String COLUMN_NAME_PATH = "PATH";
+ private static final String COLUMN_NAME_NAME = "NAME";
+
+ public StudyMaterialDatabaseController(@NotNull Context context) {
+ super(context.getApplicationContext(), DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ Log.d("Database", "onCreate database");
+ db.execSQL("CREATE TABLE " + DATABASE_NAME + " (" +
+ COLUMN_NAME_ID + " INTEGER PRIMARY KEY, " +
+ COLUMN_NAME_SUBJECT + " TEXT, " +
+ COLUMN_NAME_TERM + " INTEGER, " +
+ COLUMN_NAME_VERSION + " INTEGER, " +
+ COLUMN_NAME_PATH + " TEXT, " +
+ COLUMN_NAME_NAME + " TEXT" + ");");
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldDATABASE_VERSION, int newDATABASE_VERSION) {
+ db.execSQL("DROP TABLE IF EXISTS " + DATABASE_NAME);
+ onCreate(db);
+ }
+
+ @NotNull
+ private StudyMaterial getStudyMaterialByCursor(@NotNull Cursor cursor) {
+ int id = cursor.getInt(cursor.getColumnIndex(COLUMN_NAME_ID));
+ String subject = cursor.getString(cursor.getColumnIndex(COLUMN_NAME_SUBJECT));
+ int term = cursor.getInt(cursor.getColumnIndex(COLUMN_NAME_TERM));
+ int version = cursor.getInt(cursor.getColumnIndex(COLUMN_NAME_VERSION));
+ String path = cursor.getString(cursor.getColumnIndex(COLUMN_NAME_PATH));
+ String name = cursor.getString(cursor.getColumnIndex(COLUMN_NAME_NAME));
+ return new StudyMaterial(name, subject, term, path, version, id);
+ }
+
+ public void addStudyMaterial(@NotNull StudyMaterial studyMaterial) {
+ try (SQLiteDatabase database = this.getWritableDatabase()) {
+ ContentValues values = new ContentValues();
+ values.put(COLUMN_NAME_ID, studyMaterial.getId());
+ values.put(COLUMN_NAME_SUBJECT, studyMaterial.getSubject());
+ values.put(COLUMN_NAME_TERM, studyMaterial.getTerm());
+ values.put(COLUMN_NAME_VERSION, studyMaterial.getVersion());
+ values.put(COLUMN_NAME_PATH, studyMaterial.getPath());
+ values.put(COLUMN_NAME_NAME, studyMaterial.getName());
+ long rowId = database.insert(DATABASE_NAME, null, values);
+ Log.d("Database", "inserted row number " + rowId);
+ }
+ }
+
+ @NotNull
+ public List getStudyMaterialsBySubject(@NotNull String subject) {
+ String query = "SELECT * FROM " + DATABASE_NAME +
+ " WHERE " + COLUMN_NAME_SUBJECT + "=" + "?";
+ List studyMaterials = new ArrayList<>();
+ String[] selectionArgs = new String[]{String.valueOf(subject)};
+
+ try (SQLiteDatabase database = this.getReadableDatabase();
+ Cursor cursor = database.rawQuery(query, selectionArgs)) {
+ if (cursor != null && cursor.moveToFirst()) {
+ do {
+ studyMaterials.add(getStudyMaterialByCursor(cursor));
+ } while (cursor.moveToNext());
+ }
+ }
+
+ return studyMaterials;
+ }
+
+ @NotNull
+ public List getStudyMaterialsByTerm(int term) {
+ String query = "SELECT * FROM " + DATABASE_NAME +
+ " WHERE " + COLUMN_NAME_TERM + "=" + "?";
+ List studyMaterials = new ArrayList<>();
+ String[] selectionArgs = new String[]{String.valueOf(term)};
+
+ try (SQLiteDatabase database = this.getReadableDatabase();
+ Cursor cursor = database.rawQuery(query, selectionArgs)) {
+ if (cursor != null && cursor.moveToFirst()) {
+ do {
+ studyMaterials.add(getStudyMaterialByCursor(cursor));
+ } while (cursor.moveToNext());
+ }
+ }
+
+ return studyMaterials;
+ }
+
+ public void deleteStudyMaterialById(int id) {
+ try (SQLiteDatabase database = this.getWritableDatabase()) {
+ database.delete(DATABASE_NAME, COLUMN_NAME_ID + " = ?",
+ new String[]{String.valueOf(id)});
+ }
+ }
+
+ @NotNull
+ public StudyMaterial getStudyMaterialById(int id) {
+ String query = "SELECT * FROM " + DATABASE_NAME + " WHERE " + COLUMN_NAME_ID + "=?";
+ String[] selectionArgs = new String[]{String.valueOf(id)};
+
+ try (SQLiteDatabase database = this.getReadableDatabase();
+ Cursor cursor = database.rawQuery(query, selectionArgs)) {
+ cursor.moveToFirst();
+ return getStudyMaterialByCursor(cursor);
+ }
+ }
+
+ public void editStudyMaterialById(int id, @NotNull String subject, int term) {
+ try (SQLiteDatabase database = this.getWritableDatabase()) {
+ ContentValues values = new ContentValues();
+ values.put(COLUMN_NAME_SUBJECT, subject);
+ values.put(COLUMN_NAME_TERM, term);
+ database.update(DATABASE_NAME, values, COLUMN_NAME_ID + " = ?",
+ new String[]{String.valueOf(id)});
+ }
+ }
+
+ @NotNull
+ public List getUpdatableStudyMaterials() {
+ String query = "SELECT * FROM " + DATABASE_NAME +
+ " WHERE " + COLUMN_NAME_VERSION + "!=" + "?";
+ List studyMaterials = new ArrayList<>();
+ String[] selectionArgs = new String[]{"-1"};
+
+ try (SQLiteDatabase database = this.getReadableDatabase();
+ Cursor cursor = database.rawQuery(query, selectionArgs)) {
+ if (cursor != null && cursor.moveToFirst()) {
+ do {
+ studyMaterials.add(getStudyMaterialByCursor(cursor));
+ } while (cursor.moveToNext());
+ }
+ }
+
+ return studyMaterials;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/StudyMaterialsUpdater.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/StudyMaterialsUpdater.java
new file mode 100644
index 0000000..9771c27
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/StudyMaterialsUpdater.java
@@ -0,0 +1,85 @@
+package ru.spbau.group202.notdeadbydeadline.controller;
+
+
+import android.os.AsyncTask;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import ru.spbau.group202.notdeadbydeadline.model.StudyMaterial;
+import ru.spbau.group202.notdeadbydeadline.model.utilities.ModelUtils;
+import ru.spbau.group202.notdeadbydeadline.model.utilities.StudyMaterialsSourceAccessException;
+import ru.spbau.group202.notdeadbydeadline.model.utilities.StudyMaterialsUpdatingException;
+import ru.spbau.group202.notdeadbydeadline.model.utilities.WebStudyMaterialException;
+
+
+public class StudyMaterialsUpdater extends AsyncTask {
+ private StudyMaterialsUpdatingException updatingException;
+ private String urlContent;
+ private static final String STUDY_MATERIAL_SOURCE = "https://cdkrot.me/";
+
+ public StudyMaterialsUpdater() throws StudyMaterialsSourceAccessException {
+ try {
+ urlContent = IOUtils.toString(new URL(STUDY_MATERIAL_SOURCE));
+ } catch (IOException exception) {
+ throw new StudyMaterialsSourceAccessException();
+ }
+ }
+
+ public StudyMaterialsUpdatingException getError() {
+ return updatingException;
+ }
+
+ @Override
+ protected Void doInBackground(StudyMaterial... studyMaterials) {
+ ArrayList errors = new ArrayList<>();
+ for (StudyMaterial studyMaterial : studyMaterials) {
+ try {
+ updateStudyMaterial(studyMaterial);
+ } catch (WebStudyMaterialException exception) {
+ errors.add(exception);
+ }
+ }
+
+ if (!errors.isEmpty()) {
+ updatingException = new StudyMaterialsUpdatingException(errors);
+ }
+
+ return null;
+ }
+
+ private void updateStudyMaterial(StudyMaterial studyMaterial) throws WebStudyMaterialException {
+ Pattern urlPattern = Pattern.compile("href=\"(/.+/" + studyMaterial.getName() +
+ "/(\\d+)\\.pdf)\"");
+ Matcher urlMatcher = urlPattern.matcher(urlContent);
+
+ if (!urlMatcher.find()) {
+ throw new WebStudyMaterialException("No such study material at study material's resource.",
+ ModelUtils.STUDY_MATERIAL_FIELDS_TO_STRING_LIST.apply(studyMaterial));
+ }
+ int newVersion = Integer.valueOf(urlMatcher.group(2));
+ if (newVersion > studyMaterial.getVersion()) {
+ try {
+ URL studyMaterialUrl = new URL(STUDY_MATERIAL_SOURCE + urlMatcher.group(1));
+ File studyMaterialFile = new File(studyMaterial.getPath(), studyMaterial.getName());
+ FileUtils.copyURLToFile(studyMaterialUrl, studyMaterialFile);
+ } catch (MalformedURLException e) {
+ throw new WebStudyMaterialException("Malformed URL.",
+ ModelUtils.STUDY_MATERIAL_FIELDS_TO_STRING_LIST.apply(studyMaterial));
+ } catch (IOException e) {
+ throw new WebStudyMaterialException("Unable to download study material.",
+ ModelUtils.STUDY_MATERIAL_FIELDS_TO_STRING_LIST.apply(studyMaterial));
+ }
+ studyMaterial.setVersion(Integer.valueOf(urlMatcher.group(2)));
+ }
+ }
+
+}
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/SubjectDatabaseController.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/SubjectDatabaseController.java
new file mode 100644
index 0000000..b4576ef
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/SubjectDatabaseController.java
@@ -0,0 +1,112 @@
+package ru.spbau.group202.notdeadbydeadline.controller;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import ru.spbau.group202.notdeadbydeadline.model.CreditByAcceptedHomeworks;
+import ru.spbau.group202.notdeadbydeadline.model.CreditByPercent;
+import ru.spbau.group202.notdeadbydeadline.model.CreditFormEnum;
+import ru.spbau.group202.notdeadbydeadline.model.SubjectCredit;
+import ru.spbau.group202.notdeadbydeadline.model.utilities.UnrecognizedCreditFormException;
+
+
+public class SubjectDatabaseController extends SQLiteOpenHelper {
+ private static final String DATABASE_NAME = "Subjects";
+ private static final int DATABASE_VERSION = 1;
+ private static final String COLUMN_NAME_SUBJECT = "SUBJECT";
+ private static final String COLUMN_NAME_CREDIT_FORM = "CREDIT_FORM";
+ private static final String COLUMN_NAME_PERCENT_FOR_CREDIT = "PERCENT_FOR_CREDIT";
+
+ public SubjectDatabaseController(@NotNull Context context) {
+ super(context.getApplicationContext(), DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ Log.d("Database", "onCreate database");
+ db.execSQL("CREATE TABLE " + DATABASE_NAME + " (" +
+ COLUMN_NAME_SUBJECT + " TEXT PRIMARY KEY, " +
+ COLUMN_NAME_CREDIT_FORM + " INTEGER, " +
+ COLUMN_NAME_PERCENT_FOR_CREDIT + " REAL" + ");");
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldDATABASE_VERSION, int newDATABASE_VERSION) {
+ db.execSQL("DROP TABLE IF EXISTS " + DATABASE_NAME);
+ onCreate(db);
+ }
+
+ public void addSubject(@NotNull String subject, @NotNull CreditFormEnum creditForm, double percentForCredit) {
+ try (SQLiteDatabase database = this.getWritableDatabase()) {
+ ContentValues values = new ContentValues();
+ values.put(COLUMN_NAME_SUBJECT, subject);
+ values.put(COLUMN_NAME_CREDIT_FORM, creditForm.ordinal());
+ values.put(COLUMN_NAME_PERCENT_FOR_CREDIT, percentForCredit);
+ long rowId = database.insert(DATABASE_NAME, null, values);
+ Log.d("Database", "inserted row number " + rowId);
+ }
+ }
+
+ @NotNull
+ public List getAllSubjects() {
+ ArrayList subjects = new ArrayList<>();
+ try (SQLiteDatabase database = this.getReadableDatabase();
+ Cursor cursor = database.rawQuery("SELECT " + COLUMN_NAME_SUBJECT
+ + " FROM " + DATABASE_NAME, null)) {
+ if (cursor != null && cursor.moveToFirst()) {
+ do {
+ String subject = cursor.getString(cursor.getColumnIndex(COLUMN_NAME_SUBJECT));
+ subjects.add(subject);
+ } while (cursor.moveToNext());
+ }
+ }
+
+ return subjects;
+ }
+
+ @NotNull
+ private SubjectCredit getSubjectCreditByCursor(@NotNull Cursor cursor) throws UnrecognizedCreditFormException {
+ int creditForm = cursor.getInt(cursor.getColumnIndex(COLUMN_NAME_CREDIT_FORM));
+ double percentForCredit = cursor.getInt(cursor.getColumnIndex(COLUMN_NAME_PERCENT_FOR_CREDIT));
+
+ switch (CreditFormEnum.values()[creditForm]) {
+ case BY_PERCENT:
+ return new CreditByPercent(percentForCredit);
+ case BY_ACCEPTED_HOMEWORKS:
+ return new CreditByAcceptedHomeworks();
+ case NOT_STATED:
+ return new SubjectCredit();
+ default:
+ throw new UnrecognizedCreditFormException();
+ }
+ }
+
+ @NotNull
+ public SubjectCredit getSubjectCredit(@NotNull String subject) throws UnrecognizedCreditFormException {
+ String query = "SELECT * FROM " + DATABASE_NAME + " WHERE " + COLUMN_NAME_SUBJECT + "=?";
+ String[] selectionArgs = new String[]{subject};
+
+ try (SQLiteDatabase database = this.getReadableDatabase();
+ Cursor cursor = database.rawQuery(query, selectionArgs)) {
+ return getSubjectCreditByCursor(cursor);
+ }
+ }
+
+ public void setSubjectCreditForm(@NotNull String subject, @NotNull CreditFormEnum credit) {
+ try (SQLiteDatabase database = this.getWritableDatabase()) {
+ ContentValues values = new ContentValues();
+ values.put(COLUMN_NAME_CREDIT_FORM, credit.ordinal());
+ database.update(DATABASE_NAME, values, COLUMN_NAME_SUBJECT + " = ?",
+ new String[]{subject});
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/WebStudyMaterialsListGetter.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/WebStudyMaterialsListGetter.java
new file mode 100644
index 0000000..2ae5bf0
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/controller/WebStudyMaterialsListGetter.java
@@ -0,0 +1,58 @@
+package ru.spbau.group202.notdeadbydeadline.controller;
+
+
+import android.os.AsyncTask;
+
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+import org.jsoup.select.Elements;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import ru.spbau.group202.notdeadbydeadline.model.utilities.AsyncTaskResult;
+import ru.spbau.group202.notdeadbydeadline.model.utilities.StudyMaterialsSourceAccessException;
+
+
+public class WebStudyMaterialsListGetter
+ extends AsyncTask[], StudyMaterialsSourceAccessException>> {
+ private static final String STUDY_MATERIAL_SOURCE = "https://cdkrot.me/";
+
+ @Override
+ protected AsyncTaskResult[], StudyMaterialsSourceAccessException> doInBackground(Void... voids) {
+ @SuppressWarnings("unchecked")
+ ArrayList[] materialsByTerms = new ArrayList[7];
+ for (int term = 0; term < materialsByTerms.length; term++) {
+ materialsByTerms[term] = new ArrayList<>();
+ }
+
+ try {
+ Document html = Jsoup.connect(STUDY_MATERIAL_SOURCE).get();
+ Elements materialsUrl = html.select("a[href$=.pdf]");
+
+ Pattern urlPattern = Pattern.compile(".+/(.+/.*?term(\\d).+)/\\d+\\.pdf");
+ Pattern noTermPattern = Pattern.compile(STUDY_MATERIAL_SOURCE + "(.+)/\\d+\\.pdf");
+ for (Element materialUrl : materialsUrl) {
+ String absUrl = materialUrl.attr("abs:href");
+ Matcher urlMatcher = urlPattern.matcher(absUrl);
+ if (urlMatcher.find()) {
+ materialsByTerms[Integer.valueOf(urlMatcher.group(2))].add(urlMatcher.group(1));
+ } else {
+ Matcher noTermMatcher = noTermPattern.matcher(absUrl);
+ if (noTermMatcher.find()) {
+ materialsByTerms[0].add(noTermMatcher.group(1));
+ }
+
+ }
+ }
+ } catch (IOException exception) {
+ return new AsyncTaskResult<>(new StudyMaterialsSourceAccessException());
+ }
+
+ return new AsyncTaskResult<>(materialsByTerms);
+ }
+}
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/Class.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/Class.java
new file mode 100644
index 0000000..57678c4
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/Class.java
@@ -0,0 +1,96 @@
+package ru.spbau.group202.notdeadbydeadline.model;
+
+import android.os.Bundle;
+
+import org.jetbrains.annotations.NotNull;
+import org.joda.time.LocalTime;
+import org.joda.time.format.DateTimeFormat;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class Class extends ScheduleEntry {
+ private String subject;
+ private String auditorium;
+ private String teacher;
+ private LocalTime time;
+ private int dayOfWeek;
+ private int id;
+ private WeekParityEnum weekParity;
+
+ public Class(@NotNull String subject, int dayOfWeek, int hour, int minute,
+ WeekParityEnum weekParity, String auditorium, String teacher, int id) {
+ time = new LocalTime(hour, minute);
+ this.subject = subject;
+ this.dayOfWeek = dayOfWeek;
+ this.weekParity = weekParity;
+ this.auditorium = auditorium;
+ this.teacher = teacher;
+ this.id = id;
+ }
+
+ @NotNull
+ public Bundle getDeconstructed() {
+ Bundle bundle = new Bundle();
+ bundle.putString("subject", subject);
+ bundle.putString("auditorium", auditorium);
+ bundle.putString("teacher", teacher);
+ bundle.putInt("id", id);
+ bundle.putInt("dayOfWeek", dayOfWeek);
+ bundle.putSerializable("weekParity", weekParity);
+ bundle.putSerializable("time", time);
+ return bundle;
+ }
+
+ @NotNull
+ @Override
+ protected LocalTime getTime() {
+ return time;
+ }
+
+ @NotNull
+ @Override
+ public List getScheduleDescription() {
+ ArrayList classDetails = new ArrayList<>();
+ classDetails.add(subject);
+ classDetails.add(DateTimeFormat.forPattern("HH:mm").print(time));
+ classDetails.add(teacher);
+ classDetails.add(auditorium);
+ classDetails.add(Integer.toString(id));
+ return classDetails;
+ }
+
+ public int getHour() {
+ return time.getHourOfDay();
+ }
+
+ public int getMinute() {
+ return time.getMinuteOfHour();
+ }
+
+ @NotNull
+ public String getSubject() {
+ return subject;
+ }
+
+ public String getAuditorium() {
+ return auditorium;
+ }
+
+ public String getTeacher() {
+ return teacher;
+ }
+
+ public int getDayOfWeek() {
+ return dayOfWeek;
+ }
+
+ public WeekParityEnum getWeekParity() {
+ return weekParity;
+ }
+
+ public int getId() {
+ return id;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/CreditByAcceptedHomeworks.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/CreditByAcceptedHomeworks.java
new file mode 100644
index 0000000..1e5aa4a
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/CreditByAcceptedHomeworks.java
@@ -0,0 +1,31 @@
+package ru.spbau.group202.notdeadbydeadline.model;
+
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class CreditByAcceptedHomeworks extends SubjectCredit {
+ @NotNull
+ @Override
+ protected List calculateHomeworkProgress(@NotNull List homeworks) {
+ int numberOfCheckedHomeworks = 0;
+ int numberOfAcceptedHomeworks = 0;
+
+ for (Homework homework : homeworks) {
+ if (homework.getActualScore() != -1 && homework.getExpectedScore() != -1) {
+ numberOfCheckedHomeworks++;
+ if (homework.isAccepted()) {
+ numberOfAcceptedHomeworks++;
+ }
+ }
+ }
+ double percent = numberOfCheckedHomeworks == 0 ? 1 : (double) numberOfAcceptedHomeworks / numberOfCheckedHomeworks;
+ String credit = percent == 1.0 ? "Passed class" : "Failed class";
+ int numberOfNotAcceptedHomeworks = numberOfCheckedHomeworks - numberOfAcceptedHomeworks;
+
+ return Arrays.asList("by accepted homeworks", Double.toString(percent), credit,
+ Integer.toString(numberOfNotAcceptedHomeworks));
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/CreditByPercent.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/CreditByPercent.java
new file mode 100644
index 0000000..8dc2233
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/CreditByPercent.java
@@ -0,0 +1,35 @@
+package ru.spbau.group202.notdeadbydeadline.model;
+
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class CreditByPercent extends SubjectCredit {
+ private double percentForCredit;
+
+ public CreditByPercent(double percentForCredit) {
+ this.percentForCredit = percentForCredit;
+ }
+
+ @NotNull
+ @Override
+ protected List calculateHomeworkProgress(@NotNull List homeworks) {
+ double totalPoints = 0;
+ double earnedPoints = 0;
+
+ for (Homework homework : homeworks) {
+ if (homework.getActualScore() != -1 && homework.getExpectedScore() != -1) {
+ earnedPoints += homework.getActualScore();
+ totalPoints += homework.getExpectedScore();
+ }
+ }
+
+ double percent = totalPoints == 0 ? 1 : earnedPoints / totalPoints;
+ String result = percent >= percentForCredit ? "Class passed" : "Class failed";
+ double pointsForCredit = percentForCredit * totalPoints - earnedPoints;
+ return Arrays.asList("by percent", Double.toString(percent), result,
+ pointsForCredit < 0 ? "0" : Double.toString(pointsForCredit));
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/CreditFormEnum.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/CreditFormEnum.java
new file mode 100644
index 0000000..8ce8b21
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/CreditFormEnum.java
@@ -0,0 +1,6 @@
+package ru.spbau.group202.notdeadbydeadline.model;
+
+
+public enum CreditFormEnum {
+ BY_ACCEPTED_HOMEWORKS, BY_PERCENT, NOT_STATED
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/Exam.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/Exam.java
new file mode 100644
index 0000000..654efa4
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/Exam.java
@@ -0,0 +1,106 @@
+package ru.spbau.group202.notdeadbydeadline.model;
+
+
+import android.os.Bundle;
+
+import org.jetbrains.annotations.NotNull;
+import org.joda.time.LocalDateTime;
+import org.joda.time.LocalTime;
+import org.joda.time.format.DateTimeFormat;
+
+import java.util.List;
+
+import ru.spbau.group202.notdeadbydeadline.model.utilities.ModelUtils;
+
+
+public class Exam extends ScheduleEntry {
+ private String subject;
+ private String description;
+ private LocalDateTime date;
+ private boolean isAccepted = false;
+ private ExamEnum examType;
+ private int id;
+
+ public Exam(@NotNull String subject, String description, @NotNull LocalDateTime date,
+ @NotNull ExamEnum examType, int id) {
+ this.subject = subject;
+ this.description = description;
+ this.date = date;
+ this.examType = examType;
+ this.id = id;
+ }
+
+ public boolean isAccepted() {
+ return isAccepted;
+ }
+
+ public void setAccepted(boolean isAccepted) {
+ this.isAccepted = isAccepted;
+ }
+
+ @NotNull
+ public String getSubject() {
+ return subject;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public int getYear() {
+ return date.getYear();
+ }
+
+ public int getMonth() {
+ return date.getMonthOfYear();
+ }
+
+ public int getDay() {
+ return date.getDayOfMonth();
+ }
+
+ public int getHour() {
+ return date.getHourOfDay();
+ }
+
+ public int getMinute() {
+ return date.getMinuteOfHour();
+ }
+
+ @NotNull
+ public ExamEnum getExamType() {
+ return examType;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public String getFormattedTime() {
+ return DateTimeFormat.forPattern("HH:mm").print(date);
+ }
+
+ @Override
+ @NotNull
+ public List getScheduleDescription() {
+ return ModelUtils.EXAM_FIELDS_TO_STRING_LIST.apply(this);
+ }
+
+ @NotNull
+ public Bundle getDeconstructed() {
+ Bundle bundle = new Bundle();
+ bundle.putString("subject", subject);
+ bundle.putString("description", description);
+ bundle.putSerializable("type", examType);
+ bundle.putBoolean("isAccepted", isAccepted);
+ bundle.putInt("id", id);
+ bundle.putSerializable("date", date);
+ return bundle;
+ }
+
+ @NotNull
+ @Override
+ protected LocalTime getTime() {
+ return date.toLocalTime();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/ExamEnum.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/ExamEnum.java
new file mode 100644
index 0000000..c463722
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/ExamEnum.java
@@ -0,0 +1,17 @@
+package ru.spbau.group202.notdeadbydeadline.model;
+
+
+public enum ExamEnum {
+ TEST() {
+ public String getDescription() {
+ return "Test";
+ }
+ },
+ FINAL_EXAM() {
+ public String getDescription() {
+ return "Exam";
+ }
+ };
+
+ public abstract String getDescription();
+}
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/Homework.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/Homework.java
new file mode 100644
index 0000000..01c632a
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/Homework.java
@@ -0,0 +1,189 @@
+package ru.spbau.group202.notdeadbydeadline.model;
+
+
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+
+import java.util.ArrayList;
+
+import org.jetbrains.annotations.NotNull;
+import org.joda.time.LocalDateTime;
+import org.joda.time.format.*;
+
+public class Homework implements Comparable {
+ private Deadline deadline;
+ private String subject;
+ private String description;
+ private String howToSend;
+ private int regularity;
+ private int deferral = 0;
+ private double expectedScore;
+ private double actualScore = -1;
+ private int id;
+ private ArrayList materials;
+
+ public Homework(LocalDateTime deadline, @NotNull String subject, int regularity, String description,
+ String howToSend, double expectedScore, int id, @NotNull ArrayList materials) {
+ this.deadline = new Deadline(deadline);
+ this.subject = subject;
+ this.regularity = regularity;
+ this.description = description;
+ this.howToSend = howToSend;
+ this.expectedScore = expectedScore;
+ this.id = id;
+ this.materials = materials;
+ }
+
+ public void setActualScore(double score) {
+ actualScore = score;
+ }
+
+ public void setDeferral(int deferral) {
+ deadline.deadline.plusDays(deferral);
+ assignDeferral(deferral);
+ }
+
+ public void assignDeferral(int deferral) {
+ this.deferral = deferral;
+ }
+
+ public boolean isAccepted() {
+ return actualScore >= expectedScore;
+ }
+
+ @NotNull
+ public String getFormattedDeadline() {
+ return deadline.getFormattedDeadline();
+ }
+
+ @NotNull
+ public ArrayList getDetails() {
+ ArrayList homeworkDetails = new ArrayList<>();
+ homeworkDetails.add(getDescription());
+ homeworkDetails.add(deadline.getFormattedDeadline());
+ homeworkDetails.add(getHowToSend());
+ if (getExpectedScore() == -1.0) {
+ homeworkDetails.add("Not specified");
+ } else {
+ homeworkDetails.add(Double.toString(getExpectedScore()));
+ }
+ homeworkDetails.add(Integer.toString(regularity));
+ // TODO is the following line necessary
+ homeworkDetails.addAll(materials);
+ homeworkDetails.add(Integer.toString(id));
+
+ return homeworkDetails;
+ }
+
+ @NotNull
+ public Bundle getDeconstructed() {
+ Bundle bundle = new Bundle();
+ bundle.putString("subject", subject);
+ bundle.putString("description", description);
+ bundle.putString("howToSend", howToSend);
+ bundle.putInt("id", id);
+ bundle.putInt("regularity", regularity);
+ bundle.putInt("deferral", deferral);
+ bundle.putDouble("expectedScore", expectedScore);
+ bundle.putDouble("actualScore", actualScore);
+ bundle.putStringArrayList("materials", materials);
+ bundle.putSerializable("deadline", deadline.deadline);
+ return bundle;
+ }
+
+ public Deadline getDeadline() {
+ return deadline;
+ }
+
+ @NotNull
+ public String getSubject() {
+ return subject;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public int getRegularity() {
+ return regularity;
+ }
+
+ public String getHowToSend() {
+ return howToSend;
+ }
+
+ public double getExpectedScore() {
+ return expectedScore;
+ }
+
+ public double getActualScore() {
+ return actualScore;
+ }
+
+ public int getYear() {
+ return deadline.deadline.getYear();
+ }
+
+ public int getMonth() {
+ return deadline.deadline.getMonthOfYear();
+ }
+
+ public int getDay() {
+ return deadline.deadline.getDayOfMonth();
+ }
+
+ public int getHour() {
+ return deadline.deadline.getHourOfDay();
+ }
+
+ public int getMinute() {
+ return deadline.deadline.getMinuteOfHour();
+ }
+
+ public int getDeferral() {
+ return deferral;
+ }
+
+ public boolean hasPassed() {
+ return deadline.hasPassed();
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public ArrayList getMaterials() {
+ return materials;
+ }
+
+ @NotNull
+ public Homework generateNewHomeworkById(int id) {
+ LocalDateTime newDeadline = deadline.deadline.minusDays(deferral);
+ deadline.deadline.plusWeeks(regularity);
+ return new Homework(newDeadline, subject, regularity, "", howToSend,
+ -1, id, new ArrayList<>());
+ }
+
+ @Override
+ public int compareTo(@NonNull Homework homework) {
+ return deadline.deadline.compareTo(homework.deadline.deadline);
+ }
+
+ public class Deadline {
+ private LocalDateTime deadline;
+
+ private Deadline(@NotNull LocalDateTime deadline) {
+ this.deadline = deadline;
+ }
+
+ private boolean hasPassed() {
+ return LocalDateTime.now().compareTo(deadline) > 0;
+ }
+
+ @NotNull
+ public String getFormattedDeadline() {
+ DateTimeFormatter formatter = DateTimeFormat.forPattern("dd.MM.yyyy HH:mm");
+ return formatter.print(deadline);
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/ScheduleEntry.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/ScheduleEntry.java
new file mode 100644
index 0000000..98d9ec8
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/ScheduleEntry.java
@@ -0,0 +1,19 @@
+package ru.spbau.group202.notdeadbydeadline.model;
+
+import org.jetbrains.annotations.NotNull;
+import org.joda.time.LocalTime;
+
+import java.util.List;
+
+
+public abstract class ScheduleEntry implements Comparable {
+ @NotNull
+ protected abstract LocalTime getTime();
+
+ @NotNull
+ public abstract List getScheduleDescription();
+
+ public int compareTo(@NotNull ScheduleEntry detailedEntry) {
+ return getTime().compareTo(detailedEntry.getTime());
+ }
+}
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/StudyMaterial.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/StudyMaterial.java
new file mode 100644
index 0000000..15791f5
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/StudyMaterial.java
@@ -0,0 +1,70 @@
+package ru.spbau.group202.notdeadbydeadline.model;
+
+
+import android.os.Bundle;
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+
+
+public class StudyMaterial {
+ private String subject;
+ private String path;
+ private String name;
+ private int term;
+ private int version;
+ private int id;
+
+ public StudyMaterial(@NotNull String name, @NotNull String subject, int term,
+ @NotNull String path, int version, int id) {
+ this.subject = subject;
+ this.term = term;
+ this.name = name;
+ this.path = path;
+ this.version = version;
+ this.id = id;
+ }
+
+ @NotNull
+ public String getPath() {
+ return path;
+ }
+
+ @NotNull
+ public String getName() {
+ return name;
+ }
+
+ public int getTerm() {
+ return term;
+ }
+
+ @NotNull
+ public String getSubject() {
+ return subject;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public int getVersion() {
+ return version;
+ }
+
+ public void setVersion(int version) {
+ this.version = version;
+ }
+
+ @NotNull
+ public Bundle getDeconstructed() {
+ Bundle bundle = new Bundle();
+ bundle.putString("subject", subject);
+ bundle.putString("path", path);
+ bundle.putString("name", name);
+ bundle.putInt("id", id);
+ bundle.putInt("term", term);
+ return bundle;
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/SubjectCredit.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/SubjectCredit.java
new file mode 100644
index 0000000..2411199
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/SubjectCredit.java
@@ -0,0 +1,58 @@
+package ru.spbau.group202.notdeadbydeadline.model;
+
+import org.jetbrains.annotations.NotNull;
+import org.apache.commons.collections4.ListUtils;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+
+public class SubjectCredit {
+ @NotNull
+ public List calculateProgress(@NotNull List homeworks, @NotNull List exams) {
+ return ListUtils.union(calculateHomeworkProgress(homeworks), calculateExamProgress(exams));
+ }
+
+ @NotNull
+ protected List calculateHomeworkProgress(@NotNull List homeworks) {
+ double totalPoints = 0;
+ double earnedPoints = 0;
+
+ for (Homework homework : homeworks) {
+ if (homework.getActualScore() != -1) {
+ earnedPoints += homework.getActualScore();
+ totalPoints += homework.getExpectedScore();
+ }
+ }
+
+ double percent = totalPoints == 0 ? 1 : earnedPoints / totalPoints;
+ return Arrays.asList("not stated", Double.toString(percent));
+ }
+
+ @NotNull
+ private List calculateExamProgress(@NotNull List exams) {
+ HashMap totalNumber = new HashMap<>();
+ HashMap numberOfPassed = new HashMap<>();
+
+ for (Exam exam : exams) {
+ ExamEnum kind = exam.getExamType();
+ totalNumber.put(kind, totalNumber.get(kind) + 1);
+ if (exam.isAccepted()) {
+ numberOfPassed.put(kind, numberOfPassed.get(kind) + 1);
+ }
+ }
+
+ double passedTestsPercent = totalNumber.get(ExamEnum.TEST) == 0 ? 1 :
+ (double) numberOfPassed.get(ExamEnum.TEST) / totalNumber.get(ExamEnum.TEST);
+ double passedExamsPercent = totalNumber.get(ExamEnum.FINAL_EXAM) == 0 ? 1 :
+ (double) numberOfPassed.get(ExamEnum.FINAL_EXAM) / totalNumber.get(ExamEnum.FINAL_EXAM);
+ String testCredit = passedTestsPercent == 1.0 ? "Passed class" : "Failed class";
+ String examsCredit = passedExamsPercent == 1.0 ? "Passed class" : "Failed class";
+ int numberOfNotPassedTests = totalNumber.get(ExamEnum.TEST) - numberOfPassed.get(ExamEnum.TEST);
+ int numberOfNotPassedExams = totalNumber.get(ExamEnum.FINAL_EXAM) - numberOfPassed.get(ExamEnum.FINAL_EXAM);
+
+ return Arrays.asList(Double.toString(passedTestsPercent), testCredit,
+ Integer.toString(numberOfNotPassedTests), Double.toString(passedExamsPercent), examsCredit,
+ Integer.toString(numberOfNotPassedExams));
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/WeekParityEnum.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/WeekParityEnum.java
new file mode 100644
index 0000000..0eda574
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/WeekParityEnum.java
@@ -0,0 +1,22 @@
+package ru.spbau.group202.notdeadbydeadline.model;
+
+
+public enum WeekParityEnum {
+ ON_EVEN_WEEK() {
+ public WeekParityEnum inverse() {
+ return ON_ODD_WEEK;
+ }
+ },
+ ON_ODD_WEEK() {
+ public WeekParityEnum inverse() {
+ return ON_EVEN_WEEK;
+ }
+ },
+ ALWAYS() {
+ public WeekParityEnum inverse() {
+ return ALWAYS;
+ }
+ };
+
+ public abstract WeekParityEnum inverse();
+}
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/utilities/AsyncTaskResult.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/utilities/AsyncTaskResult.java
new file mode 100644
index 0000000..e8af29f
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/utilities/AsyncTaskResult.java
@@ -0,0 +1,25 @@
+package ru.spbau.group202.notdeadbydeadline.model.utilities;
+
+import ru.spbau.group202.notdeadbydeadline.controller.Controller;
+
+
+public class AsyncTaskResult {
+ private T result;
+ private E error;
+
+ public T getResult() {
+ return result;
+ }
+
+ public E getError() {
+ return error;
+ }
+
+ public AsyncTaskResult(T result) {
+ this.result = result;
+ }
+
+ public AsyncTaskResult(E error) {
+ this.error = error;
+ }
+}
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/utilities/Function.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/utilities/Function.java
new file mode 100644
index 0000000..ff4c4cf
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/utilities/Function.java
@@ -0,0 +1,6 @@
+package ru.spbau.group202.notdeadbydeadline.model.utilities;
+
+
+public interface Function {
+ U apply(T argument);
+}
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/utilities/ModelUtils.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/utilities/ModelUtils.java
new file mode 100644
index 0000000..df806fe
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/utilities/ModelUtils.java
@@ -0,0 +1,56 @@
+package ru.spbau.group202.notdeadbydeadline.model.utilities;
+
+
+
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+
+import ru.spbau.group202.notdeadbydeadline.model.Exam;
+import ru.spbau.group202.notdeadbydeadline.model.Homework;
+import ru.spbau.group202.notdeadbydeadline.model.ScheduleEntry;
+import ru.spbau.group202.notdeadbydeadline.model.StudyMaterial;
+
+public class ModelUtils {
+ public static final Function> HW_FIELDS_TO_STRING_LIST = hw -> {
+ ArrayList fields = new ArrayList<>();
+ fields.add(hw.getDescription());
+ fields.add(hw.getFormattedDeadline());
+ fields.add(hw.getHowToSend());
+ if (hw.getExpectedScore() == -1.0) {
+ fields.add("Not specified");
+ } else {
+ fields.add(Double.toString(hw.getExpectedScore()));
+ }
+ fields.add(Integer.toString(hw.getRegularity()));
+ fields.addAll(hw.getMaterials());
+ fields.add(Integer.toString(hw.getId()));
+ return fields;
+ };
+
+ public static final Function> HW_DEADLINE_FIELDS_TO_STRING_LIST = hw ->
+ Arrays.asList(hw.getSubject(), hw.getDescription(), hw.getFormattedDeadline());
+
+ public static final Function> EXAM_FIELDS_TO_STRING_LIST = exam ->
+ Arrays.asList(exam.getSubject(), exam.getFormattedTime(), exam.getExamType().getDescription(),
+ exam.getDescription(), Integer.toString(exam.getId()));
+
+ public static final Function> STUDY_MATERIAL_FIELDS_TO_STRING_LIST =
+ material -> Arrays.asList(material.getSubject(), Integer.toString(material.getTerm()),
+ material.getPath(), material.getName(), Integer.toString(material.getId()));
+
+ public static final Function> SCHEDULE_ENTRY_TO_SCHEDULE_DESCRIPTION =
+ ScheduleEntry::getScheduleDescription;
+
+ @NotNull
+ public static List map(@NotNull List list, @NotNull Function function) {
+ List result = new ArrayList<>();
+ for(T element : list) {
+ result.add(function.apply(element));
+ }
+ return result;
+ }
+}
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/utilities/StudyMaterialsSourceAccessException.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/utilities/StudyMaterialsSourceAccessException.java
new file mode 100644
index 0000000..4f96347
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/utilities/StudyMaterialsSourceAccessException.java
@@ -0,0 +1,5 @@
+package ru.spbau.group202.notdeadbydeadline.model.utilities;
+
+
+public class StudyMaterialsSourceAccessException extends Exception {
+}
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/utilities/StudyMaterialsUpdatingException.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/utilities/StudyMaterialsUpdatingException.java
new file mode 100644
index 0000000..9f18786
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/utilities/StudyMaterialsUpdatingException.java
@@ -0,0 +1,16 @@
+package ru.spbau.group202.notdeadbydeadline.model.utilities;
+
+
+import java.util.List;
+
+public class StudyMaterialsUpdatingException extends Exception {
+ private List exceptions;
+
+ public StudyMaterialsUpdatingException(List exceptions) {
+ this.exceptions = exceptions;
+ }
+
+ public List getErrors() {
+ return exceptions;
+ }
+}
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/utilities/UnrecognizedCreditFormException.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/utilities/UnrecognizedCreditFormException.java
new file mode 100644
index 0000000..8e2fab6
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/utilities/UnrecognizedCreditFormException.java
@@ -0,0 +1,5 @@
+package ru.spbau.group202.notdeadbydeadline.model.utilities;
+
+
+public class UnrecognizedCreditFormException extends Exception {
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/utilities/UrlDownloadingException.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/utilities/UrlDownloadingException.java
new file mode 100644
index 0000000..60a0830
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/utilities/UrlDownloadingException.java
@@ -0,0 +1,5 @@
+package ru.spbau.group202.notdeadbydeadline.model.utilities;
+
+
+public class UrlDownloadingException extends Exception {
+}
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/utilities/WebStudyMaterialException.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/utilities/WebStudyMaterialException.java
new file mode 100644
index 0000000..d05846c
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/model/utilities/WebStudyMaterialException.java
@@ -0,0 +1,16 @@
+package ru.spbau.group202.notdeadbydeadline.model.utilities;
+
+
+import java.util.List;
+
+public class WebStudyMaterialException extends Exception {
+ private List studyMaterial;
+
+ public WebStudyMaterialException(String message, List studyMaterial) {
+ super(message);
+ }
+
+ public List getStudyMaterial() {
+ return studyMaterial;
+ }
+}
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/AddHomeworkActivity.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/AddHomeworkActivity.java
new file mode 100644
index 0000000..81a60b1
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/AddHomeworkActivity.java
@@ -0,0 +1,532 @@
+package ru.spbau.group202.notdeadbydeadline.ui;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.MediaStore;
+import android.provider.OpenableColumns;
+import android.support.v7.app.AppCompatActivity;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.AutoCompleteTextView;
+import android.widget.Button;
+import android.widget.DatePicker;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.TimePicker;
+import android.widget.Toast;
+
+import com.ipaulpro.afilechooser.utils.FileUtils;
+
+import org.joda.time.LocalDateTime;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import ru.spbau.group202.notdeadbydeadline.controller.Controller;
+import ru.spbau.group202.notdeadbydeadline.R;
+import ru.spbau.group202.notdeadbydeadline.ui.utilities.AbstractDatePicker;
+import ru.spbau.group202.notdeadbydeadline.ui.utilities.AbstractTimePicker;
+
+public class AddHomeworkActivity extends AppCompatActivity {
+
+ private static final String TAG = "AddHomeworkActivity";
+ private static final int REQUEST_CHOOSER = 1234;
+ public static final HomeworkFieldsAccumulator HFA = new HomeworkFieldsAccumulator();
+ private static boolean isSetTime = false;
+ private static boolean isSetDate = false;
+ private static boolean isSetSubject = false;
+ private static boolean isSetDescription = false;
+ private static boolean isSetExpectedScore = false;
+ private static boolean isSetHowToSend = false;
+ private static boolean isSetRegularity = false;
+
+ private static Bundle homeworkEntry;
+
+ private int id = -1;
+
+ public void processSubject() {
+
+ final List source = Controller.getInstance(this).getSubjectList();
+
+ final AutoCompleteTextView actv = findViewById(R.id.getSubjectACTV);
+ actv.setAdapter(new ArrayAdapter<>(this,
+ android.R.layout.simple_dropdown_item_1line, source));
+
+
+ HFA.storeSubject(actv.getText().toString());
+
+ if (id != -1 && !isSetSubject) {
+ actv.setText(homeworkEntry.getString("subject"));
+ }
+
+ actv.setOnEditorActionListener(new TextView.OnEditorActionListener() {
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ if (!source.contains(actv.getText().toString())) {
+ source.add(actv.getText().toString());
+ }
+ HFA.storeSubject((actv.getText().toString()));
+
+
+ if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER))
+ || (actionId == EditorInfo.IME_ACTION_DONE)) {
+ Log.e("TAG", "Done pressed");
+ }
+ return false;
+ }
+ });
+
+ actv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ //HFA.storeSubject((actv.getText().toString()));
+ if (!source.contains(actv.getText().toString())) {
+ source.add(actv.getText().toString());
+ }
+
+ View view1 = getCurrentFocus();
+ if (view1 != null) {
+ InputMethodManager inputManager =
+ (InputMethodManager) getSystemService(
+ Context.INPUT_METHOD_SERVICE);
+
+ if (inputManager != null) {
+ inputManager.hideSoftInputFromWindow(view1.getWindowToken(),
+ InputMethodManager.HIDE_NOT_ALWAYS);
+ }
+
+ }
+ }
+ });
+ }
+
+ public void getSubject() {
+ final AutoCompleteTextView actv = findViewById(R.id.getSubjectACTV);
+
+ HFA.storeSubject(actv.getText().toString());
+ }
+
+ public void getDescription() {
+ final EditText editText = findViewById(R.id.getDescriptionEditText);
+
+ HFA.storeDescription(editText.getText().toString());
+
+ if (id != -1 && !isSetDescription) {
+ editText.setText(homeworkEntry.getString("description"));
+ }
+ }
+
+ public void processExpectedScore() {
+ final EditText editText = findViewById(R.id.expectedScore);
+
+ if (id != -1 && !isSetExpectedScore) {
+ double score = homeworkEntry.getDouble("expectedScore");
+ if (score != -1) {
+ editText.setText(String.format(Locale.getDefault(),
+ "%s", homeworkEntry.getDouble("expectedScore")));
+ } else {
+ editText.setText(getResources().getString(R.string.not_specified));
+ }
+ }
+
+ String expectedScore = editText.getText().toString();
+ if (isParseableDouble(expectedScore)) {
+ HFA.storeExpectedScore(Double.parseDouble(expectedScore));
+ }
+ }
+
+ public void getExpectedScore() {
+ final EditText editText = findViewById(R.id.expectedScore);
+
+ String expectedScore = editText.getText().toString();
+ if (isParseableDouble(expectedScore)) {
+ HFA.storeExpectedScore(Double.parseDouble(expectedScore));
+ } else {
+ HFA.storeExpectedScore(-1.0);
+ }
+ }
+
+ public void processHowToSend() {
+ final EditText editText = findViewById(R.id.submitWayEditText);
+
+ editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER))
+ || (actionId == EditorInfo.IME_ACTION_DONE)) {
+ Log.d(TAG, "Done pressed");
+ }
+ return false;
+ }
+ });
+
+ editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ HFA.storeHowToSend(editText.getText().toString());
+
+
+ View view1 = getCurrentFocus();
+ if (view1 != null) {
+ InputMethodManager inputManager =
+ (InputMethodManager) getSystemService(
+ Context.INPUT_METHOD_SERVICE);
+ if (inputManager != null) {
+ inputManager.hideSoftInputFromWindow(view1.getWindowToken(),
+ InputMethodManager.HIDE_NOT_ALWAYS);
+ }
+ }
+ return false;
+ }
+ });
+
+
+ if (id != -1 && !isSetHowToSend) {
+ editText.setText(homeworkEntry.getString("howToSend"));
+ }
+ }
+
+ public void getHowToSend() {
+ final EditText editText = findViewById(R.id.submitWayEditText);
+
+ HFA.storeHowToSend(editText.getText().toString());
+ }
+
+ public void setTime(View view) {
+ TimePickerFragment timePickerFragment = new TimePickerFragment();
+ if (id != -1 && !isSetTime) {
+ LocalDateTime deadline = (LocalDateTime) homeworkEntry.getSerializable("deadline");
+ if (deadline != null) {
+ timePickerFragment.setValues(deadline.getHourOfDay(),
+ deadline.getMinuteOfHour());
+ }
+ }
+
+ timePickerFragment.show(getSupportFragmentManager(), "timePicker");
+ }
+
+ public void setDate(View view) {
+ DatePickerFragment datePickerFragment = new DatePickerFragment();
+ if (id != -1 && !isSetDate) {
+ LocalDateTime deadline = (LocalDateTime) homeworkEntry.getSerializable("deadline");
+ if (deadline != null) {
+ datePickerFragment.setValues(deadline.getYear(),
+ deadline.getMonthOfYear() - 1,
+ deadline.getDayOfMonth());
+ }
+ }
+
+ datePickerFragment.show(getSupportFragmentManager(), "datePicker");
+ }
+
+ public void processRegularity() {
+ EditText editText = findViewById(R.id.setRegularityEditText);
+
+ if (id != -1 && !isSetRegularity) {
+ editText.setText(String.format(Locale.getDefault(),
+ "%d", homeworkEntry.getInt("regularity")));
+ }
+
+ String regularity = editText.getText().toString();
+ if (isParseableInteger(regularity)) {
+ HFA.storeRegularity(Integer.parseInt(regularity));
+ }
+ }
+
+ public void getRegularity() {
+ EditText editText = findViewById(R.id.setRegularityEditText);
+ String regularity = editText.getText().toString();
+ if (regularity.isEmpty()) {
+ HFA.storeRegularity(0);
+ } else if (isParseableInteger(regularity)) {
+ HFA.storeRegularity(Integer.parseInt(regularity));
+ }
+ }
+
+ public void setFiles(View view) {
+ Intent getContentIntent = FileUtils.createGetContentIntent();
+
+ Intent intent = Intent.createChooser(getContentIntent, "Select a file");
+ startActivityForResult(intent, REQUEST_CHOOSER);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ switch (requestCode) {
+ case REQUEST_CHOOSER:
+ if (resultCode == RESULT_OK) {
+
+ final Uri uri = data.getData();
+ String path;
+
+ // Get the File path from the Uri
+ path = FileUtils.getPath(this, uri);
+
+ /*// Alternatively, use FileUtils.getFile(Context, Uri)
+ if (path != null && FileUtils.isLocal(path)) {
+ File file = new File(path);
+ path = file.getAbsolutePath();
+ }*/
+
+ HFA.storeMaterials(path);
+ }
+ break;
+ }
+ }
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_add_homework);
+
+ if (getSupportActionBar() != null) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+
+ HFA.clear();
+
+ id = getIntent().getIntExtra("id", -1);
+
+ if (id != -1) {
+ TextView header = findViewById(R.id.addNewHWHeader);
+ header.setText(getResources().getString(R.string.edit_hw_entry));
+ homeworkEntry = Controller.getInstance(this).homeworkController().getHomeworkById(id);
+
+ ArrayList files = homeworkEntry.getStringArrayList("materials");
+ if (files != null) {
+ for (String file : files) {
+ HFA.storeMaterials(file);
+ }
+ }
+
+ }
+
+ processSubject();
+ getDescription();
+ processExpectedScore();
+ processHowToSend();
+ processRegularity();
+
+ Button addHomeworkButton = findViewById(R.id.finishButton);
+ addHomeworkButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+
+ getDescription();
+ processSubject();
+ getSubject();
+ getExpectedScore();
+ getHowToSend();
+ getRegularity();
+
+ if (id == -1 && !HFA.isValidForAdding()) {
+ Toast.makeText(getApplicationContext(),
+ "Fill 'subject' and input correct date",
+ Toast.LENGTH_LONG).show();
+ } else if (id != -1 && !HFA.isValidForEditing()) {
+ Toast.makeText(getApplicationContext(),
+ "Fill 'subject'",
+ Toast.LENGTH_LONG).show();
+ } else {
+ if (id == -1) {
+ HFA.addNewHomework(AddHomeworkActivity.this);
+ } else {
+ HFA.editHomework(id, AddHomeworkActivity.this);
+ }
+ HFA.clear();
+ finish();
+ }
+ }
+ });
+ }
+
+ private boolean isParseableDouble(String string) {
+ try {
+ Double.parseDouble(string);
+ return true;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+
+ private boolean isParseableInteger(String string) {
+ try {
+ Integer.parseInt(string);
+ return true;
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+
+ public static class HomeworkFieldsAccumulator {
+ private String subject;
+ private String description;
+ private int year;
+ private int month;
+ private int day;
+ private int hour;
+ private int minutes;
+ private double expectedScore = Double.MIN_VALUE;
+ private int regularity = 0;
+ private String howToSend;
+ private ArrayList materials = new ArrayList<>();
+
+ public void storeSubject(String subject) {
+ if (this.subject != null && !this.subject.equals(subject)) {
+ isSetSubject = true;
+ }
+ this.subject = subject == null ? "" : subject;
+ }
+
+ public void storeDescription(String description) {
+ if (this.description != null && !this.description.equals(description)) {
+ isSetDescription = true;
+ }
+ this.description = description == null ? "" : description;
+ }
+
+ public void storeExpectedScore(double expectedScore) {
+ if (this.expectedScore != expectedScore) {
+ isSetExpectedScore = true;
+ }
+ this.expectedScore = expectedScore;
+ }
+
+ public void storeDate(int year, int month, int day) {
+ this.year = year;
+ this.month = month;
+ this.day = day;
+ }
+
+ public void storeTime(int hour, int minutes) {
+ this.hour = hour;
+ this.minutes = minutes;
+ }
+
+ public void storeRegularity(int regularity) {
+ if (this.regularity != regularity) {
+ isSetRegularity = true;
+ }
+ this.regularity = regularity;
+ }
+
+ public void storeHowToSend(String howToSend) {
+ if (this.howToSend != null && !this.howToSend.equals(howToSend)) {
+ isSetHowToSend = true;
+ }
+ this.howToSend = howToSend == null ? "" : howToSend;
+ }
+
+ public void storeMaterials(String filepath) {
+ materials.add(filepath);
+ }
+
+ public void addNewHomework(Context context) {
+
+ if (description == null) {
+ description = " ";
+ }
+
+ if (howToSend == null) {
+ howToSend = " ";
+ }
+
+ Controller.getInstance(context).homeworkController()
+ .addHomework(new LocalDateTime(year, month, day, hour, minutes), subject,
+ regularity, description, howToSend, expectedScore, materials);
+ }
+
+ public void editHomework(int id, Context context) {
+ if (description == null) {
+ description = " ";
+ }
+
+ if (howToSend == null) {
+ howToSend = " ";
+ }
+
+ if (!isSetDate) {
+ LocalDateTime deadline = (LocalDateTime) homeworkEntry.getSerializable("deadline");
+ if (deadline != null) {
+ year = deadline.getYear();
+ month = deadline.getMonthOfYear();
+ day = deadline.getDayOfMonth();
+ }
+ }
+
+ if (!isSetTime) {
+ LocalDateTime deadline = (LocalDateTime) homeworkEntry.getSerializable("deadline");
+ if (deadline != null) {
+ hour = deadline.getHourOfDay();
+ minutes = deadline.getMinuteOfHour();
+ }
+ }
+
+ Controller.getInstance(context).homeworkController()
+ .editHomeworkById(id, new LocalDateTime(year, month, day, hour, minutes),
+ subject, regularity, description, howToSend, expectedScore, materials);
+
+ }
+
+ public boolean isValidForAdding() {
+ return subject != null && isSetDate && isSetTime;
+ }
+
+ public boolean isValidForEditing() {
+ return subject.trim().length() > 0;
+ }
+
+ public void clear() {
+ subject = null;
+ description = null;
+ howToSend = null;
+ expectedScore = Double.MIN_VALUE;
+ year = 0;
+ month = 0;
+ day = 0;
+ hour = 0;
+ minutes = 0;
+ regularity = 0;
+ materials.clear();
+
+ isSetTime = false;
+ isSetDate = false;
+ isSetExpectedScore = false;
+ isSetSubject = false;
+ isSetDescription = false;
+ isSetHowToSend = false;
+ isSetRegularity = false;
+ }
+
+ }
+
+ public static class TimePickerFragment extends AbstractTimePicker {
+
+ @Override
+ public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
+ isSetTime = true;
+ HFA.storeTime(hourOfDay, minute);
+ }
+
+ }
+
+ public static class DatePickerFragment extends AbstractDatePicker {
+ @Override
+ public void onDateSet(DatePicker view, int year, int month, int day) {
+ isSetDate = true;
+ HFA.storeDate(year, month + 1, day);
+ }
+ }
+
+
+}
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/AddScheduleEntryActivity.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/AddScheduleEntryActivity.java
new file mode 100644
index 0000000..1980cde
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/AddScheduleEntryActivity.java
@@ -0,0 +1,357 @@
+package ru.spbau.group202.notdeadbydeadline.ui;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.PopupMenu;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.inputmethod.EditorInfo;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.Spinner;
+import android.widget.TextView;
+import android.widget.TimePicker;
+import android.widget.Toast;
+
+import org.joda.time.LocalTime;
+
+import ru.spbau.group202.notdeadbydeadline.R;
+import ru.spbau.group202.notdeadbydeadline.controller.Controller;
+import ru.spbau.group202.notdeadbydeadline.model.WeekParityEnum;
+import ru.spbau.group202.notdeadbydeadline.ui.utilities.AbstractTimePicker;
+import ru.spbau.group202.notdeadbydeadline.ui.utilities.WeekDayEnum;
+
+public class AddScheduleEntryActivity extends AppCompatActivity {
+
+ private static final String TAG = "AddSchEntryActivity";
+ private static final ScheduleEntryFieldsAccumulator SEFA = new ScheduleEntryFieldsAccumulator();
+ private static boolean isSetTime = false;
+ private static boolean isSetSubject = false;
+ private static boolean isSetTeacher = false;
+ private static boolean isSetAuditorium = false;
+ private static boolean isSetParity = false;
+
+ private int id;
+ private Bundle scheduleEntry;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_add_schedule_entry);
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ SEFA.clear();
+
+ id = getIntent().getIntExtra("id", -1);
+
+ if (getSupportActionBar() != null) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setTitle(id == -1 ?
+ getResources().getString(R.string.add_schedule_entry_header)
+ : getResources().getString(R.string.edit_schedule_entry));
+ }
+
+ if (id != -1) {
+ scheduleEntry = Controller.getInstance(this).scheduleController().getClassById(id);
+ SEFA.dayOfWeek = scheduleEntry.getInt("dayOfWeek");
+ LocalTime localTime = (LocalTime) scheduleEntry.getSerializable("time");
+ if (localTime != null) {
+ SEFA.hour = localTime.getHourOfDay();
+ SEFA.minute = localTime.getMinuteOfHour();
+ }
+ }
+
+ TextView header = findViewById(R.id.addNewSEHeader);
+ header.setText(id == -1 ? getResources().getString(R.string
+ .add_schedule_entry_header) :
+ getResources().getString(R.string
+ .edit_schedule_entry));
+
+ getSubject();
+ getAuditorium();
+ getTeacher();
+ getParity();
+
+ Button addSEbutton = findViewById(R.id.scheduleFinishButton);
+ addSEbutton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+
+ getSubject();
+ getAuditorium();
+ getTeacher();
+ getParity();
+
+ if (id == -1 && SEFA.isValidForAddingSE()) {
+ SEFA.addScheduleEntry(AddScheduleEntryActivity.this);
+ SEFA.clear();
+ finish();
+ } else if (id != -1 && SEFA.isValidForEditing()){
+ SEFA.editScheduleEntry(id, AddScheduleEntryActivity.this);
+ SEFA.clear();
+ finish();
+ } else {
+ Toast.makeText(getApplicationContext(),
+ "Fill subject, weekday and time",
+ Toast.LENGTH_LONG).show();
+ }
+ }
+ });
+
+ }
+
+ public void setWeekDay(View view) {
+ Button button = findViewById(R.id.scheduleSetWeekDayButton);
+ PopupMenu popup = new PopupMenu(AddScheduleEntryActivity.this, button);
+ popup.getMenuInflater()
+ .inflate(R.menu.week_day_menu, popup.getMenu());
+ //registering popup with OnMenuItemClickListener
+ popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ SEFA.storeWeekDay(item.getTitle().toString());// item.getTitle()
+ return true;
+ }
+ });
+
+ popup.show(); //showing popup menu
+ }
+
+ public void scheduleSetTime(View view) {
+ TimePickerFragment timePickerFragment = new TimePickerFragment();
+ if (id != -1 && !isSetTime) {
+ LocalTime localTime = (LocalTime) scheduleEntry.getSerializable("time");
+ if (localTime != null) {
+ timePickerFragment.setValues(localTime.getHourOfDay(),
+ localTime.getMinuteOfHour());
+ }
+ }
+
+
+ timePickerFragment.show(getSupportFragmentManager(), "timePicker");
+ }
+
+ public void getSubject() {
+ EditText editText = findViewById(R.id.scheduleGetSubjectET);
+
+ SEFA.storeSubject(editText.getText().toString());
+
+ if (id != -1 && !isSetSubject) {
+ editText.setText(scheduleEntry.getString("subject"));
+ }
+
+ editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ SEFA.storeSubject((editText.getText().toString()));
+
+
+ if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER))
+ || (actionId == EditorInfo.IME_ACTION_DONE)) {
+ Log.e("TAG", "Done pressed");
+ }
+ return false;
+ }
+ });
+ }
+
+ public void getTeacher() {
+ EditText editText = findViewById(R.id.scheduleTeacherET);
+
+ SEFA.storeTeacher(editText.getText().toString());
+
+ if (id != -1 && !isSetTeacher) {
+ editText.setText(scheduleEntry.getString("teacher"));
+ }
+
+ editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ SEFA.storeTeacher((editText.getText().toString()));
+
+
+ if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER))
+ || (actionId == EditorInfo.IME_ACTION_DONE)) {
+ Log.e("TAG", "Done pressed");
+ }
+ return false;
+ }
+ });
+ }
+
+ public void getAuditorium() {
+ EditText editText = findViewById(R.id.scheduleAuditoriumET);
+
+ SEFA.storeAuditorium(editText.getText().toString());
+
+ if (id != -1 && !isSetAuditorium) {
+ editText.setText(scheduleEntry.getString("auditorium"));
+ }
+
+ editText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ SEFA.storeAuditorium((editText.getText().toString()));
+
+
+ if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER))
+ || (actionId == EditorInfo.IME_ACTION_DONE)) {
+ Log.e("TAG", "Done pressed");
+ }
+ return false;
+ }
+ });
+ }
+
+ public void getParity() {
+ String parity[] = {"even weeks", "odd weeks", "every week"};
+ ArrayAdapter adapter = new ArrayAdapter(this,
+ android.R.layout.simple_spinner_item, parity);
+ adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+
+ Spinner spinner = findViewById(R.id.weekParitySpinner);
+ spinner.setAdapter(adapter);
+ spinner.setPrompt("Week Parity");
+ if (id == -1) {
+ spinner.setSelection(2);
+ } else {
+ WeekParityEnum entryParity = isSetParity ? SEFA.parity : (WeekParityEnum) scheduleEntry
+ .getSerializable("weekParity");
+ if (entryParity != null) {
+ switch (entryParity) {
+ case ALWAYS:
+ spinner.setSelection(2);
+ break;
+ case ON_EVEN_WEEK:
+ spinner.setSelection(0);
+ break;
+ case ON_ODD_WEEK:
+ spinner.setSelection(1);
+ break;
+ }
+ }
+
+ }
+ spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView> parent, View view,
+ int position, long id) {
+ String text = parent.getItemAtPosition(position).toString();
+ switch (text) {
+ case "even weeks":
+ SEFA.storeParity(WeekParityEnum.ON_EVEN_WEEK);
+ break;
+ case "odd weeks":
+ SEFA.storeParity(WeekParityEnum.ON_ODD_WEEK);
+ break;
+ case "every week":
+ SEFA.storeParity(WeekParityEnum.ALWAYS);
+ break;
+ default:
+ Log.e(TAG,"wrong week parity type");
+ }
+ }
+ @Override
+ public void onNothingSelected(AdapterView> arg0) {
+ }
+ });
+ }
+
+
+ public static class ScheduleEntryFieldsAccumulator{
+ private String subject = null;
+ private String teacher = null;
+ private String auditorium = null;
+ private String weekDay = null;
+ private WeekParityEnum parity;
+ private int hour;
+ private int minute;
+ private int dayOfWeek; // used for editing
+
+ public void storeWeekDay(String weekDay) {
+ this.weekDay = weekDay;
+ }
+
+ public void storeSubject(String subject) {
+ if (this.subject != null && !this.subject.equals(subject)) {
+ isSetSubject = true;
+ }
+ this.subject = subject;
+ }
+
+ public void storeAuditorium(String auditorium) {
+ if (this.auditorium != null && !this.auditorium.equals(auditorium)) {
+ isSetAuditorium = true;
+ }
+ this.auditorium = auditorium;
+ }
+
+ public void storeTeacher(String teacher) {
+ if (this.teacher != null && !this.teacher.equals(teacher)) {
+ isSetTeacher = true;
+ }
+ this.teacher = teacher;
+ }
+
+ public void storeTime(int hour, int minute) {
+ this.hour = hour;
+ this.minute = minute;
+ }
+
+ public void storeParity(WeekParityEnum parity) {
+ if (this.parity != null && !this.parity.equals(parity)) {
+ isSetParity = true;
+ }
+ this.parity = parity;
+ }
+
+ public boolean isValidForAddingSE() {
+ return subject != null && weekDay != null && isSetTime;
+ }
+
+ public boolean isValidForEditing() {
+ return subject != null && subject.trim().length() > 0;
+ }
+
+ public void addScheduleEntry(Context context) {
+ Controller.getInstance(context).scheduleController().addClass(subject,
+ WeekDayEnum.valueOf(weekDay).ordinal(), hour, minute,
+ parity, auditorium, teacher);
+ }
+
+ public void editScheduleEntry(int id, Context context) {
+ Controller.getInstance(context).scheduleController().editClassById(id, subject,
+ weekDay == null ? dayOfWeek :
+ WeekDayEnum.valueOf(weekDay).ordinal(), hour, minute,
+ parity, auditorium, teacher);
+ }
+
+ public void clear() {
+ subject = null;
+ teacher = null;
+ auditorium = null;
+ weekDay = null;
+ hour = 0;
+ minute = 0;
+
+ isSetParity = false;
+ isSetTeacher = false;
+ isSetAuditorium = false;
+ isSetSubject = false;
+ isSetTime = false;
+ }
+
+ }
+
+ public static class TimePickerFragment extends AbstractTimePicker {
+ @Override
+ public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
+ isSetTime = true;
+ SEFA.storeTime(hourOfDay, minute);
+ }
+ }
+
+}
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/DeadlinesActivity.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/DeadlinesActivity.java
new file mode 100644
index 0000000..0702ab2
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/DeadlinesActivity.java
@@ -0,0 +1,283 @@
+package ru.spbau.group202.notdeadbydeadline.ui;
+
+import android.content.Intent;
+import android.graphics.Typeface;
+import android.os.Bundle;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.StyleSpan;
+import android.support.design.widget.NavigationView;
+import android.support.v4.view.GravityCompat;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v7.app.ActionBarDrawerToggle;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.TableLayout;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jetbrains.annotations.NotNull;
+
+import org.joda.time.LocalDate;
+import org.joda.time.DateTimeConstants;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+
+import ru.spbau.group202.notdeadbydeadline.controller.Controller;
+import ru.spbau.group202.notdeadbydeadline.R;
+import ru.spbau.group202.notdeadbydeadline.ui.utilities.ListViewUtility;
+
+public class DeadlinesActivity extends AppCompatActivity
+ implements NavigationView.OnNavigationItemSelectedListener {
+
+ private static final String TAG = "DeadlinesActivity";
+
+ private LocalDate localDate;
+
+ private void outputDeadlineByDay( int dayNumber ) {
+
+ List> deadlinesDetails;
+
+ LocalDate weekDay = localDate.withDayOfWeek(dayNumber);
+ Controller.getInstance(this).homeworkController().generateHomeworks();
+ deadlinesDetails = Controller.getInstance(this).homeworkController()
+ .getDeadlinesByDay(weekDay);
+
+ ArrayList formattedDeadlines = new ArrayList<>();
+ for (List deadlineDetails : deadlinesDetails) {
+ SpannableStringBuilder stringBuilder =
+ new SpannableStringBuilder(deadlineDetails.get(2)
+ .split("\\s+")[1]);
+
+
+ int position = stringBuilder.length();
+ stringBuilder.append(" ");
+ stringBuilder.append(deadlineDetails.get(0));
+ stringBuilder.setSpan(new StyleSpan(Typeface.BOLD),
+ position, stringBuilder.length(),
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ stringBuilder.append("\n");
+ stringBuilder.append(deadlineDetails.get(1));
+
+ formattedDeadlines.add(stringBuilder);
+ }
+
+ ListView lv;
+ switch (dayNumber) {
+ case DateTimeConstants.MONDAY:
+ lv = findViewById(R.id.deadlinesMondayList);
+ break;
+ case DateTimeConstants.TUESDAY:
+ lv = findViewById(R.id.deadlinesTuesdayList);
+ break;
+ case DateTimeConstants.WEDNESDAY:
+ lv = findViewById(R.id.deadlinesWednesdayList);
+ break;
+ case DateTimeConstants.THURSDAY:
+ lv = findViewById(R.id.deadlinesThursdayList);
+ break;
+ case DateTimeConstants.FRIDAY:
+ lv = findViewById(R.id.deadlinesFridayList);
+ break;
+ case DateTimeConstants.SATURDAY:
+ lv = findViewById(R.id.deadlinesSaturdayList);
+ break;
+ case DateTimeConstants.SUNDAY:
+ lv = findViewById(R.id.deadlinesSundayList);
+ break;
+ default:
+ Log.e(TAG, "Wrong dayNumber parameter in OutputDeadlineByDay");
+ return;
+ }
+
+ ArrayAdapter adapter = new ArrayAdapter<>(this,
+ R.layout.custom_deadline_listview_entry,
+ formattedDeadlines);
+ lv.setAdapter(adapter);
+ }
+
+ private void setListViewsHeightAllDays() {
+ ListView monday = findViewById(R.id.deadlinesMondayList);
+ ListView tuesday = findViewById(R.id.deadlinesTuesdayList);
+ ListView wednesday = findViewById(R.id.deadlinesWednesdayList);
+ ListView thursday = findViewById(R.id.deadlinesThursdayList);
+ ListView friday = findViewById(R.id.deadlinesFridayList);
+ ListView saturday = findViewById(R.id.deadlinesSaturdayList);
+
+ ListViewUtility.setTwoListViewsHeight(monday, thursday);
+ ListViewUtility.setTwoListViewsHeight(tuesday, friday);
+ ListViewUtility.setTwoListViewsHeight(wednesday, saturday);
+ }
+
+ private void outputDeadlines() {
+
+ final TableLayout table = findViewById(R.id.deadlinesTableLayout);
+ table.setColumnShrinkable(0, true);
+ table.setColumnShrinkable(1, true);
+
+ for (int i = 1; i <= 7; ++i)
+ outputDeadlineByDay(i);
+
+ setListViewsHeightAllDays();
+ }
+
+ private void setHeaders() {
+
+ DateTimeFormatter formatter = DateTimeFormat.forPattern("dd.MM.yyyy");
+
+ String monday = getResources().getString(R.string.monday_for_deadlines,
+ formatter.print(localDate.withDayOfWeek(1)));
+ String tuesday = getResources().getString(R.string.tuesday_for_deadlines,
+ formatter.print(localDate.withDayOfWeek(2)));
+ String wednesday = getResources().getString(R.string.wednesday_for_deadlines,
+ formatter.print(localDate.withDayOfWeek(3)));
+ String thursday = getResources().getString(R.string.thursday_for_deadlines,
+ formatter.print(localDate.withDayOfWeek(4)));
+ String friday = getResources().getString(R.string.friday_for_deadlines,
+ formatter.print(localDate.withDayOfWeek(5)));
+ String saturday = getResources().getString(R.string.saturday_for_deadlines,
+ formatter.print(localDate.withDayOfWeek(6)));
+ String sunday = getResources().getString(R.string.sunday_for_deadlines,
+ formatter.print(localDate.withDayOfWeek(7)));
+
+ TextView tv = findViewById(R.id.deadlinesMondayHeader);
+ tv.setText(monday);
+ tv = findViewById(R.id.deadlinesTuesdayHeader);
+ tv.setText(tuesday);
+ tv = findViewById(R.id.deadlinesWednesdayHeader);
+ tv.setText(wednesday);
+ tv = findViewById(R.id.deadlinesThursdayHeader);
+ tv.setText(thursday);
+ tv = findViewById(R.id.deadlinesFridayHeader);
+ tv.setText(friday);
+ tv = findViewById(R.id.deadlinesSaturdayHeader);
+ tv.setText(saturday);
+ tv = findViewById(R.id.deadlinesSundayHeader);
+ tv.setText(sunday);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_deadlines);
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
+ this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
+ drawer.addDrawerListener(toggle);
+ toggle.syncState();
+
+ NavigationView navigationView = findViewById(R.id.nav_view);
+ navigationView.setNavigationItemSelectedListener(this);
+
+ setTitle(getString(R.string.deadlineHeader));
+
+ localDate = (LocalDate) getIntent().getSerializableExtra("date");
+
+ setHeaders();
+ outputDeadlines();
+
+ Button nextWeekButton = findViewById(R.id.nextWeekButton);
+ nextWeekButton.setText(R.string.next_week);
+ nextWeekButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+
+ localDate = localDate.plusDays(7);
+ setHeaders();
+ outputDeadlines();
+ }
+ });
+
+ Button button = findViewById(R.id.previousWeekButton);
+ button.setText(getResources().getString(R.string.previous_week));
+ button.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+
+ localDate = localDate.minusDays(7);
+ setHeaders();
+ outputDeadlines();
+ }
+ });
+ }
+
+ @Override
+ public void onBackPressed() {
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ if (drawer.isDrawerOpen(GravityCompat.START)) {
+ drawer.closeDrawer(GravityCompat.START);
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.deadlines, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle action bar item clicks here. The action bar will
+ // automatically handle clicks on the Home/Up button, so long
+ // as you specify a parent activity in AndroidManifest.xml.
+ int id = item.getItemId();
+
+ //noinspection SimplifiableIfStatement
+ if (id == R.id.action_settings) {
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ @SuppressWarnings("StatementWithEmptyBody")
+ @Override
+ public boolean onNavigationItemSelected(@NotNull MenuItem item) {
+ // Handle navigation view item clicks here.
+ int id = item.getItemId();
+
+ if (id == R.id.nav_main) {
+ Intent intent = new Intent(this, MainActivity.class);
+ startActivity(intent);
+ } else if (id == R.id.nav_deadlines) {
+ Intent intent = new Intent(this, DeadlinesActivity.class);
+ intent.putExtra("date", new LocalDate());
+ startActivityForResult(intent, 1);
+ } else if (id == R.id.nav_homework) {
+ Intent intent = new Intent(this, HomeworkActivity.class);
+ startActivityForResult(intent, 1);
+ } else if (id == R.id.nav_schedule) {
+ Intent intent = new Intent(this, ScheduleActivity.class);
+ intent.putExtra("date", new LocalDate());
+ startActivityForResult(intent, 1);
+
+ } /*else if (id == R.id.nav_studymaterials) {
+ Intent intent = new Intent(this, StudyMaterialsActivity.class);
+ startActivityForResult(intent, 1);
+ }*/
+
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ drawer.closeDrawer(GravityCompat.START);
+ return true;
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ outputDeadlines();
+ }
+}
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/DisplayHomeworkActivity.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/DisplayHomeworkActivity.java
new file mode 100644
index 0000000..646eccc
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/DisplayHomeworkActivity.java
@@ -0,0 +1,115 @@
+package ru.spbau.group202.notdeadbydeadline.ui;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.PopupMenu;
+import android.support.v7.widget.Toolbar;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.ListView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import ru.spbau.group202.notdeadbydeadline.controller.Controller;
+import ru.spbau.group202.notdeadbydeadline.R;
+import ru.spbau.group202.notdeadbydeadline.ui.utilities.AttachmentsDialogFragment;
+
+
+public class DisplayHomeworkActivity extends AppCompatActivity {
+
+
+ private void outputHomeworksBySubject(String subject) {
+ Controller.getInstance(this).homeworkController().generateHomeworks();
+ List> formattedHomeworksDetails =
+ Controller.getInstance(this).homeworkController().getHomeworksBySubject(subject);
+
+ ListView homeworksListView = findViewById(R.id.homeworksListView);
+ HomeworkListViewAdapter adapter1 = new HomeworkListViewAdapter(this, formattedHomeworksDetails);
+ homeworksListView.setAdapter(adapter1);
+ }
+
+ private void processOnLongTapHomework() {
+ ListView homeworksListView = findViewById(R.id.homeworksListView);
+ homeworksListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
+ @Override
+ public boolean onItemLongClick(AdapterView> parent, View view, int position, long id) {
+ PopupMenu popup = new PopupMenu(DisplayHomeworkActivity.this, view);
+ popup.getMenuInflater()
+ .inflate(R.menu.homework_listview_item_menu, popup.getMenu());
+ //registering popup with OnMenuItemClickListener
+ popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ if (item.getTitle().toString().equals(getResources()
+ .getString(R.string.lv_entry_edit))) {
+ List detailedEntryList = (List) parent.getItemAtPosition(position);
+ int id = Integer.parseInt(detailedEntryList.get(detailedEntryList.size() - 1));
+ Intent intent = new Intent(DisplayHomeworkActivity.this, AddHomeworkActivity.class);
+ intent.putExtra("id", id);
+ startActivityForResult(intent, 1);
+ return true;
+ } else if (item.getTitle().toString().equals(getResources()
+ .getString(R.string.lv_entry_delete))) {
+ List detailedEntryList = (List) parent.getItemAtPosition(position);
+ Controller.getInstance(DisplayHomeworkActivity.this).homeworkController()
+ .deleteHomeworkById(Integer.parseInt(detailedEntryList.get(detailedEntryList.size() - 1)));
+ outputHomeworks();
+ return true;
+ } else if (item.getTitle().toString().equals(getResources()
+ .getString(R.string.lv_entry_open_attached))) {
+ List detailedEntryList = (List) parent.getItemAtPosition(position);
+ AttachmentsDialogFragment cdf = new AttachmentsDialogFragment();
+ Bundle homeworkEntry = Controller.getInstance(DisplayHomeworkActivity.this).homeworkController()
+ .getHomeworkById(Integer.parseInt(detailedEntryList
+ .get(detailedEntryList.size() - 1)));
+ ArrayList src = homeworkEntry.getStringArrayList("materials");
+ cdf.setFilepaths(src);
+ cdf.show(getSupportFragmentManager(), "files");
+ }
+
+ return false;
+ }
+ });
+
+ popup.show(); //showing popup menu
+ return true;
+ }
+ });
+ }
+
+
+
+ private void outputHomeworks() {
+ String subject = getIntent().getStringExtra("SUBJECT_NAME");
+ setTitle(subject);
+
+ outputHomeworksBySubject(subject);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ outputHomeworks();
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_display_homework);
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+ if (getSupportActionBar() != null) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ }
+
+ String subject = getIntent().getStringExtra("SUBJECT_NAME");
+ setTitle(subject);
+
+ //outputHomeworksBySubject(subject);
+ outputHomeworks();
+ processOnLongTapHomework();
+ }
+
+}
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/HomeworkActivity.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/HomeworkActivity.java
new file mode 100644
index 0000000..7629120
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/HomeworkActivity.java
@@ -0,0 +1,146 @@
+package ru.spbau.group202.notdeadbydeadline.ui;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.design.widget.FloatingActionButton;
+import android.view.View;
+import android.support.design.widget.NavigationView;
+import android.support.v4.view.GravityCompat;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v7.app.ActionBarDrawerToggle;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import org.jetbrains.annotations.NotNull;
+import org.joda.time.LocalDate;
+
+import java.util.List;
+
+import ru.spbau.group202.notdeadbydeadline.controller.Controller;
+import ru.spbau.group202.notdeadbydeadline.R;
+
+public class HomeworkActivity extends AppCompatActivity
+ implements NavigationView.OnNavigationItemSelectedListener {
+
+ private void displaySubjects() {
+ List subjects = Controller.getInstance(this).getSubjectList();
+
+ final ListView lv = findViewById(R.id.subjectListView);
+ ArrayAdapter adapter = new ArrayAdapter<>(this,
+ R.layout.custom_textview_for_bigger_listview, subjects);
+ lv.setAdapter(adapter);
+ lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ Intent intent = new Intent(getApplicationContext(), DisplayHomeworkActivity.class);
+ intent.putExtra("SUBJECT_NAME", (String) lv.getItemAtPosition(position));
+ startActivity(intent);
+ }
+ });
+ }
+
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ displaySubjects();
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_homework);
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ FloatingActionButton fab = findViewById(R.id.fab);
+ fab.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(getApplicationContext(), AddHomeworkActivity.class);
+ startActivityForResult(intent, 1);
+ }
+ });
+
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
+ this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
+ drawer.addDrawerListener(toggle);
+ toggle.syncState();
+
+ NavigationView navigationView = findViewById(R.id.nav_view);
+ navigationView.setNavigationItemSelectedListener(this);
+
+ setTitle("Homeworks");
+
+ displaySubjects();
+ }
+
+ @Override
+ public void onBackPressed() {
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ if (drawer.isDrawerOpen(GravityCompat.START)) {
+ drawer.closeDrawer(GravityCompat.START);
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.homework, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle action bar item clicks here. The action bar will
+ // automatically handle clicks on the Home/Up button, so long
+ // as you specify a parent activity in AndroidManifest.xml.
+ int id = item.getItemId();
+
+ //noinspection SimplifiableIfStatement
+ if (id == R.id.action_settings) {
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ @SuppressWarnings("StatementWithEmptyBody")
+ @Override
+ public boolean onNavigationItemSelected(@NotNull MenuItem item) {
+ // Handle navigation view item clicks here.
+ int id = item.getItemId();
+
+
+ if (id == R.id.nav_main) {
+ Intent intent = new Intent(this, MainActivity.class);
+ startActivity(intent);
+ } else if (id == R.id.nav_deadlines) {
+ Intent intent = new Intent(this, DeadlinesActivity.class);
+ intent.putExtra("date", new LocalDate());
+ startActivityForResult(intent, 1);
+ } else if (id == R.id.nav_homework) {
+ Intent intent = new Intent(this, HomeworkActivity.class);
+ startActivityForResult(intent, 1);
+ } else if (id == R.id.nav_schedule) {
+ Intent intent = new Intent(this, ScheduleActivity.class);
+ intent.putExtra("date", new LocalDate());
+ startActivityForResult(intent, 1);
+
+ }/* else if (id == R.id.nav_studymaterials) {
+ Intent intent = new Intent(this, StudyMaterialsActivity.class);
+ startActivityForResult(intent, 1);
+ }*/
+
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ drawer.closeDrawer(GravityCompat.START);
+ return true;
+ }
+}
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/HomeworkListViewAdapter.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/HomeworkListViewAdapter.java
new file mode 100644
index 0000000..c93c707
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/HomeworkListViewAdapter.java
@@ -0,0 +1,103 @@
+package ru.spbau.group202.notdeadbydeadline.ui;
+
+import android.content.Context;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.StyleSpan;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import ru.spbau.group202.notdeadbydeadline.R;
+
+import static android.graphics.Typeface.BOLD;
+
+public class HomeworkListViewAdapter extends BaseAdapter {
+ private LayoutInflater lInflater;
+ private List> detailedEntries;
+
+ HomeworkListViewAdapter(Context context,
+ List> detailedEntries) {
+ this.detailedEntries = detailedEntries;
+ lInflater = (LayoutInflater) context
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ @Override
+ public int getCount() {
+ return detailedEntries.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return detailedEntries.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public View getView(int position, View convertView, ViewGroup parent) {
+
+ View view = convertView;
+ if (view == null) {
+ view = lInflater.inflate(R.layout.custom_homework_listview_item, parent, false);
+ }
+
+ ArrayList detailedEntry = (ArrayList) getItem(position);
+
+ String deadlinesField = "\nDeadline: ";
+ String descriptionFiled = "\nDescription: ";
+ String submitField = "\nSubmit: ";
+ String expectedScoreField = "\nExpected Score: ";
+ String regularityField = "\nRegularity: ";
+
+ SpannableStringBuilder stringBuilder = new SpannableStringBuilder(descriptionFiled);
+ stringBuilder.setSpan(new StyleSpan(BOLD),
+ 0, stringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ stringBuilder.append(detailedEntry.get(0));
+ stringBuilder.append(deadlinesField);
+ stringBuilder.setSpan(new StyleSpan(BOLD),
+ stringBuilder.length() - deadlinesField.length(),
+ stringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ stringBuilder.append(detailedEntry.get(1));
+ stringBuilder.append(submitField);
+ stringBuilder.setSpan(new StyleSpan(BOLD),
+ stringBuilder.length() - submitField.length(),
+ stringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ stringBuilder.append(detailedEntry.get(2));
+ stringBuilder.append(expectedScoreField);
+ stringBuilder.setSpan(new StyleSpan(BOLD),
+ stringBuilder.length() - expectedScoreField.length(),
+ stringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ stringBuilder.append(detailedEntry.get(3));
+ stringBuilder.append(regularityField);
+ stringBuilder.setSpan(new StyleSpan(BOLD),
+ stringBuilder.length() - regularityField.length(),
+ stringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ if (detailedEntry.get(4).trim().equals("0")) {
+ stringBuilder.append("none");
+ } else if (detailedEntry.get(4).trim().equals("1")) {
+ stringBuilder.append(detailedEntry.get(4));
+ stringBuilder.append(" week");
+ } else {
+ stringBuilder.append(detailedEntry.get(4));
+ stringBuilder.append(" weeks");
+ }
+
+
+ ((TextView) view.findViewById(R.id.list_item_hw)).setText(stringBuilder);
+
+ return view;
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/MainActivity.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/MainActivity.java
new file mode 100644
index 0000000..77c36f8
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/MainActivity.java
@@ -0,0 +1,232 @@
+package ru.spbau.group202.notdeadbydeadline.ui;
+
+import android.content.Intent;
+import android.graphics.Typeface;
+import android.os.Bundle;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.ForegroundColorSpan;
+import android.text.style.RelativeSizeSpan;
+import android.text.style.StyleSpan;
+import android.support.design.widget.NavigationView;
+import android.support.v4.view.GravityCompat;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v7.app.ActionBarDrawerToggle;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import net.danlew.android.joda.JodaTimeAndroid;
+
+import org.jetbrains.annotations.NotNull;
+import org.joda.time.LocalDate;
+import org.joda.time.LocalDateTime;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import ru.spbau.group202.notdeadbydeadline.controller.Controller;
+import ru.spbau.group202.notdeadbydeadline.R;
+
+public class MainActivity extends AppCompatActivity
+ implements NavigationView.OnNavigationItemSelectedListener {
+
+ private void outputCurrentDate() {
+ LocalDateTime currentDate = LocalDateTime.now();
+
+ StringBuilder dateStringBuilder =
+ new StringBuilder(Integer.toString(currentDate.getDayOfMonth()));
+ dateStringBuilder.append("\n");
+ dateStringBuilder.append(currentDate.monthOfYear().getAsText());
+ dateStringBuilder.append("\n");
+ int pos = dateStringBuilder.length();
+ dateStringBuilder.append(currentDate.dayOfWeek().getAsText());
+ String dateString = dateStringBuilder.toString();
+
+ SpannableString date = new SpannableString(dateString);
+ date.setSpan(new RelativeSizeSpan(3f), 0, 2, 0);
+ date.setSpan(new StyleSpan(Typeface.ITALIC), pos, dateString.length(), 0);
+
+ TextView tv = findViewById(R.id.currentDate);
+ tv.setText(date);
+ tv.setFocusable(false);
+ }
+
+ private void outputDeadlines() {
+ Controller.getInstance(this).homeworkController().generateHomeworks();
+ List> deadlinesDetails = Controller.getInstance(this).homeworkController()
+ .getDeadlinesByDay(LocalDate.now());
+
+
+ LocalDateTime ldt = LocalDateTime.now().plusDays(1);
+ deadlinesDetails.addAll(Controller.getInstance(this).homeworkController()
+ .getDeadlinesByDay(ldt.toLocalDate()));
+
+
+ ArrayList formattedDeadlines = new ArrayList<>();
+ for (List deadlineDetails : deadlinesDetails) {
+ SpannableStringBuilder stringBuilder = new SpannableStringBuilder(
+ deadlineDetails.get(2));
+ DateTimeFormatter formatter = DateTimeFormat.forPattern("dd.MM.yyyy HH:mm");
+ LocalDateTime deadline = formatter.parseLocalDateTime(deadlineDetails.get(2));
+ LocalDate deadlineDate = new LocalDate(deadline);
+
+ if (deadlineDate.compareTo(LocalDate.now()) <= 0
+ && deadline.compareTo(LocalDateTime.now()) < 0) {
+ stringBuilder.setSpan(new ForegroundColorSpan(android.graphics.Color.rgb(172, 7, 7)),
+ 0, stringBuilder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ int position = stringBuilder.length();
+ stringBuilder.append(" ");
+ stringBuilder.append(deadlineDetails.get(0));
+ stringBuilder.setSpan(new StyleSpan(Typeface.BOLD),
+ position, stringBuilder.length(),
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ if (!deadlineDetails.get(1).isEmpty()) {
+ stringBuilder.append("\n");
+ stringBuilder.append(deadlineDetails.get(1));
+ }
+
+ formattedDeadlines.add(stringBuilder);
+ }
+
+ ListView lv = findViewById(R.id.deadlinesList2);
+ ArrayAdapter adapter = new ArrayAdapter<>(this,
+ R.layout.custom_mainscreen_listview_entry,
+ formattedDeadlines);
+ lv.setAdapter(adapter);
+ }
+
+ private void outputTodaySchedule() {
+ LocalDate localDate = LocalDate.now();
+ List> scheduleDetails = Controller.getInstance(this).scheduleController()
+ .getScheduleByDay(localDate);
+ /*List> scheduleDetails = Controller.ScheduleController.getScheduleByDayOfWeek(localDate.getDayOfWeek() - 1,
+ WeekParityEnum.values()[localDate.getWeekOfWeekyear() % 2]);*/
+
+ ArrayList formattedSchedule = new ArrayList<>();
+ for (int i = 0; i < scheduleDetails.size(); i++) {
+ List schDetails = scheduleDetails.get(i);
+ SpannableStringBuilder stringBuilder =
+ new SpannableStringBuilder(schDetails.get(1));
+
+ stringBuilder.append(" ");
+ int position = stringBuilder.length();
+ stringBuilder.append(schDetails.get(0));
+ stringBuilder.setSpan(new StyleSpan(Typeface.BOLD),
+ position, stringBuilder.length(),
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ stringBuilder.append(", \n");
+ stringBuilder.append(schDetails.get(2));
+ stringBuilder.append(", ");
+ stringBuilder.append(schDetails.get(3));
+
+ formattedSchedule.add(stringBuilder);
+ }
+
+ ListView lv = findViewById(R.id.scheduleListViewMainScreen);
+ ArrayAdapter adapter = new ArrayAdapter<>(this,
+ R.layout.custom_mainscreen_listview_entry,
+ formattedSchedule);
+ lv.setAdapter(adapter);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ JodaTimeAndroid.init(this);
+
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
+ this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
+ drawer.addDrawerListener(toggle);
+ toggle.syncState();
+
+ NavigationView navigationView = findViewById(R.id.nav_view);
+ navigationView.setNavigationItemSelectedListener(this);
+
+ outputCurrentDate();
+ outputDeadlines();
+ outputTodaySchedule();
+ }
+
+ @Override
+ public void onBackPressed() {
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ if (drawer.isDrawerOpen(GravityCompat.START)) {
+ drawer.closeDrawer(GravityCompat.START);
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.main, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle action bar item clicks here. The action bar will
+ // automatically handle clicks on the Home/Up button, so long
+ // as you specify a parent activity in AndroidManifest.xml.
+ int id = item.getItemId();
+
+ //noinspection SimplifiableIfStatement
+ if (id == R.id.action_settings) {
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ @SuppressWarnings("StatementWithEmptyBody")
+ @Override
+ public boolean onNavigationItemSelected(@NotNull MenuItem item) {
+ // Handle navigation view item clicks here.
+ int id = item.getItemId();
+
+ if (id == R.id.nav_main) {
+ Intent intent = new Intent(this, MainActivity.class);
+ startActivity(intent);
+ } else if (id == R.id.nav_deadlines) {
+ Intent intent = new Intent(this, DeadlinesActivity.class);
+ intent.putExtra("date", new LocalDate());
+ startActivityForResult(intent, 1);
+ } else if (id == R.id.nav_homework) {
+ Intent intent = new Intent(this, HomeworkActivity.class);
+ startActivityForResult(intent, 1);
+ } else if (id == R.id.nav_schedule) {
+ Intent intent = new Intent(this, ScheduleActivity.class);
+ intent.putExtra("date", new LocalDate());
+ startActivityForResult(intent, 1);
+ } /*else if (id == R.id.nav_studymaterials) {
+ Intent intent = new Intent(this, StudyMaterialsActivity.class);
+ startActivityForResult(intent, 1);
+ }*/
+
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ drawer.closeDrawer(GravityCompat.START);
+ return true;
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ outputDeadlines();
+ outputTodaySchedule();
+ }
+}
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/ScheduleActivity.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/ScheduleActivity.java
new file mode 100644
index 0000000..535dc5c
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/ScheduleActivity.java
@@ -0,0 +1,328 @@
+package ru.spbau.group202.notdeadbydeadline.ui;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.design.widget.FloatingActionButton;
+import android.support.v7.widget.PopupMenu;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.StyleSpan;
+import android.util.Log;
+import android.view.View;
+import android.support.design.widget.NavigationView;
+import android.support.v4.view.GravityCompat;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v7.app.ActionBarDrawerToggle;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.TableLayout;
+import android.widget.TextView;
+
+import org.jetbrains.annotations.NotNull;
+import org.joda.time.DateTimeConstants;
+import org.joda.time.LocalDate;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import ru.spbau.group202.notdeadbydeadline.controller.Controller;
+import ru.spbau.group202.notdeadbydeadline.R;
+import ru.spbau.group202.notdeadbydeadline.ui.utilities.ListViewUtility;
+
+public class ScheduleActivity extends AppCompatActivity
+ implements NavigationView.OnNavigationItemSelectedListener {
+
+ private static final String TAG = "ScheduleActivity";
+
+ private LocalDate localDate;
+
+ private void outputScheduleByDay(int dayNumber) {
+ List> scheduleDetails = Controller.getInstance(this).scheduleController()
+ .getScheduleByDay(localDate.plusDays(dayNumber));
+ //List> scheduleDetails = Controller.ScheduleController.getScheduleByDayOfWeek(dayNumber,
+ //WeekParityEnum.values()[localDate.getWeekOfWeekyear() % 2]);
+
+ /*ArrayList formattedSchedule = new ArrayList<>();
+ for (int i = 0; i < scheduleDetails.size(); i++) {
+ List schDetails = scheduleDetails.get(i);
+ SpannableStringBuilder stringBuilder =
+ new SpannableStringBuilder(schDetails.get(1));
+
+ stringBuilder.append(" ");
+ int position = stringBuilder.length();
+ stringBuilder.append(schDetails.get(0));
+ stringBuilder.setSpan(new StyleSpan(android.graphics.Typeface.BOLD),
+ position, stringBuilder.length(),
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ stringBuilder.append(", \n");
+ stringBuilder.append(schDetails.get(2));
+ stringBuilder.append(", ");
+ stringBuilder.append(schDetails.get(3));
+
+ formattedSchedule.add(stringBuilder);
+ }*/
+
+ ListView lv;
+ switch ((localDate.getDayOfWeek() + dayNumber) % 7) {
+ case DateTimeConstants.MONDAY:
+ lv = findViewById(R.id.scheduleMondayList);
+ break;
+ case DateTimeConstants.TUESDAY:
+ lv = findViewById(R.id.scheduleTuesdayList);
+ break;
+ case DateTimeConstants.WEDNESDAY:
+ lv = findViewById(R.id.scheduleWednesdayList);
+ break;
+ case DateTimeConstants.THURSDAY:
+ lv = findViewById(R.id.scheduleThursdayList);
+ break;
+ case DateTimeConstants.FRIDAY:
+ lv = findViewById(R.id.scheduleFridayList);
+ break;
+ case DateTimeConstants.SATURDAY:
+ lv = findViewById(R.id.scheduleSaturdayList);
+ break;
+ case DateTimeConstants.SUNDAY:
+ return;
+ default:
+ Log.e(TAG, "Wrong dayNumber parameter in OutputScheduleByDay");
+ return;
+ }
+
+ ScheduleListViewAdapter adapter = new ScheduleListViewAdapter(this, scheduleDetails);
+ lv.setAdapter(adapter);
+ }
+
+ private void setHeaders() {
+
+ String monday = getResources().getString(R.string.monday);
+ String tuesday = getResources().getString(R.string.tuesday);
+ String wednesday = getResources().getString(R.string.wednesday);
+ String thursday = getResources().getString(R.string.thursday);
+ String friday = getResources().getString(R.string.friday);
+ String saturday = getResources().getString(R.string.saturday);
+
+ TextView tv = findViewById(R.id.scheduleMondayHeader);
+ tv.setText(monday);
+ tv = findViewById(R.id.scheduleTuesdayHeader);
+ tv.setText(tuesday);
+ tv = findViewById(R.id.scheduleWednesdayHeader);
+ tv.setText(wednesday);
+ tv = findViewById(R.id.scheduleThursdayHeader);
+ tv.setText(thursday);
+ tv = findViewById(R.id.scheduleFridayHeader);
+ tv.setText(friday);
+ tv = findViewById(R.id.scheduleSaturdayHeader);
+ tv.setText(saturday);
+ }
+
+ private void setListViewsHeightAllDays() {
+ ListView monday = findViewById(R.id.scheduleMondayList);
+ ListView tuesday = findViewById(R.id.scheduleTuesdayList);
+ ListView wednesday = findViewById(R.id.scheduleWednesdayList);
+ ListView thursday = findViewById(R.id.scheduleThursdayList);
+ ListView friday = findViewById(R.id.scheduleFridayList);
+ ListView saturday = findViewById(R.id.scheduleSaturdayList);
+
+ ListViewUtility.setTwoListViewsHeight(monday, thursday);
+ ListViewUtility.setTwoListViewsHeight(tuesday, friday);
+ ListViewUtility.setTwoListViewsHeight(wednesday, saturday);
+ }
+
+ private void outputSchedule() {
+ final TableLayout table = findViewById(R.id.scheduleTableLayout);
+ table.setColumnShrinkable(0, true);
+ table.setColumnShrinkable(1, true);
+
+ for (int i = 0; i < 7; ++i)
+ outputScheduleByDay(i);
+
+ setListViewsHeightAllDays();
+ }
+
+ private void processOnLongTapScheduleEntry(int listViewId) {
+ ListView scheduleListView = findViewById(listViewId);
+ scheduleListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
+ @Override
+ public boolean onItemLongClick(AdapterView> parent, View view, int position, long id) {
+ PopupMenu popup = new PopupMenu(ScheduleActivity.this, view);
+ popup.getMenuInflater()
+ .inflate(R.menu.schedule_listview_item_menu, popup.getMenu());
+ //registering popup with OnMenuItemClickListener
+ popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
+ public boolean onMenuItemClick(MenuItem item) {
+ if (item.getTitle().toString().equals(getResources()
+ .getString(R.string.lv_entry_edit))) {
+ // TODO call edit (which is yet nonexistent)
+ List detailedEntryList = (List) parent.getItemAtPosition(position);
+ Intent intent = new Intent(ScheduleActivity.this,
+ AddScheduleEntryActivity.class);
+ int id = Integer.parseInt(detailedEntryList
+ .get(detailedEntryList.size() - 1));
+ intent.putExtra("id", id);
+ startActivityForResult(intent, 1);
+
+ return true;
+ } else if (item.getTitle().toString().equals(getResources()
+ .getString(R.string.lv_entry_delete))) {
+ List detailedEntryList = (List) parent.getItemAtPosition(position);
+ Controller.getInstance(ScheduleActivity.this).scheduleController()
+ .deleteClassById(Integer
+ .parseInt(detailedEntryList.get(detailedEntryList.size() - 1)));
+ outputSchedule();
+ return true;
+ }
+
+ return false;
+ }
+ });
+
+ popup.show(); //showing popup menu
+ return true;
+ }
+ });
+
+ }
+
+ private void processListviewLongTapForAllWeekdays() {
+ processOnLongTapScheduleEntry(R.id.scheduleMondayList);
+ processOnLongTapScheduleEntry(R.id.scheduleTuesdayList);
+ processOnLongTapScheduleEntry(R.id.scheduleWednesdayList);
+ processOnLongTapScheduleEntry(R.id.scheduleThursdayList);
+ processOnLongTapScheduleEntry(R.id.scheduleFridayList);
+ processOnLongTapScheduleEntry(R.id.scheduleSaturdayList);
+ }
+
+
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_schedule);
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ FloatingActionButton fab = findViewById(R.id.addScheduleEntryButton);
+ fab.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Intent intent = new Intent(getApplicationContext(), AddScheduleEntryActivity.class);
+ startActivityForResult(intent, 1);
+ }
+ });
+
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
+ this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
+ drawer.addDrawerListener(toggle);
+ toggle.syncState();
+
+ NavigationView navigationView = findViewById(R.id.nav_view);
+ navigationView.setNavigationItemSelectedListener(this);
+
+ localDate = (LocalDate) getIntent().getSerializableExtra("date");
+
+ setTitle("Schedule");
+ setHeaders();
+ outputSchedule();
+
+ Button nextWeekButton = findViewById(R.id.scheduleNextWeekButton);
+ nextWeekButton.setText(R.string.next_week);
+ nextWeekButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+
+ localDate = localDate.plusDays(7);
+ setHeaders();
+ outputSchedule();
+ }
+ });
+
+ Button button = findViewById(R.id.schedulePreviousWeekButton);
+ button.setText(getResources().getString(R.string.previous_week));
+ button.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+
+ localDate = localDate.minusDays(7);
+ setHeaders();
+ outputSchedule();
+ }
+ });
+
+ processListviewLongTapForAllWeekdays();
+ }
+
+ @Override
+ public void onBackPressed() {
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ if (drawer.isDrawerOpen(GravityCompat.START)) {
+ drawer.closeDrawer(GravityCompat.START);
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.schedule, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle action bar item clicks here. The action bar will
+ // automatically handle clicks on the Home/Up button, so long
+ // as you specify a parent activity in AndroidManifest.xml.
+ int id = item.getItemId();
+
+ //noinspection SimplifiableIfStatement
+ if (id == R.id.action_settings) {
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ @SuppressWarnings("StatementWithEmptyBody")
+ @Override
+ public boolean onNavigationItemSelected(@NotNull MenuItem item) {
+ // Handle navigation view item clicks here.
+ int id = item.getItemId();
+
+ if (id == R.id.nav_main) {
+ Intent intent = new Intent(this, MainActivity.class);
+ startActivity(intent);
+ } else if (id == R.id.nav_deadlines) {
+ Intent intent = new Intent(this, DeadlinesActivity.class);
+ intent.putExtra("date", new LocalDate());
+ startActivityForResult(intent, 1);
+ } else if (id == R.id.nav_homework) {
+ Intent intent = new Intent(this, HomeworkActivity.class);
+ startActivityForResult(intent, 1);
+ } else if (id == R.id.nav_schedule) {
+ Intent intent = new Intent(this, ScheduleActivity.class);
+ intent.putExtra("date", new LocalDate());
+ startActivityForResult(intent, 1);
+ } /*else if (id == R.id.nav_studymaterials) {
+ Intent intent = new Intent(this, StudyMaterialsActivity.class);
+ startActivityForResult(intent, 1);
+ }*/
+
+ DrawerLayout drawer = findViewById(R.id.drawer_layout);
+ drawer.closeDrawer(GravityCompat.START);
+ return true;
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ outputSchedule();
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/ScheduleListViewAdapter.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/ScheduleListViewAdapter.java
new file mode 100644
index 0000000..8d82a5c
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/ScheduleListViewAdapter.java
@@ -0,0 +1,79 @@
+package ru.spbau.group202.notdeadbydeadline.ui;
+
+import android.content.Context;
+import android.graphics.Typeface;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.style.StyleSpan;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import ru.spbau.group202.notdeadbydeadline.R;
+
+public class ScheduleListViewAdapter extends BaseAdapter {
+
+ private LayoutInflater lInflater;
+ private List> detailedEntries;
+
+ ScheduleListViewAdapter(Context context,
+ List> detailedEntries) {
+ this.detailedEntries = detailedEntries;
+ lInflater = (LayoutInflater) context
+ .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ }
+
+ @Override
+ public int getCount() {
+ return detailedEntries.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return detailedEntries.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public View getView(int position, View convertView, ViewGroup parent) {
+
+ View view = convertView;
+ if (view == null) {
+ //view = lInflater.inflate(R.layout.custom_schedule_listview_item, parent, false);
+ view = lInflater.inflate(R.layout.custom_deadline_listview_entry, parent, false);
+ }
+
+ ArrayList detailedEntry = (ArrayList) getItem(position);
+
+ SpannableStringBuilder stringBuilder = new SpannableStringBuilder(detailedEntry.get(1));
+ stringBuilder.append(" ");
+ int pos = stringBuilder.length();
+ stringBuilder.append(detailedEntry.get(0));
+ stringBuilder.setSpan(new StyleSpan(Typeface.BOLD),
+ pos, stringBuilder.length(),
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ if (!detailedEntry.get(2).isEmpty() || !detailedEntry.get(3).isEmpty()) {
+ stringBuilder.append(", \n");
+ if (!detailedEntry.get(2).isEmpty()) {
+ stringBuilder.append(detailedEntry.get(2));
+ stringBuilder.append(", ");
+ }
+ stringBuilder.append(detailedEntry.get(3));
+ }
+
+
+ ((TextView) view.findViewById(android.R.id.text2)).setText(stringBuilder);
+
+ return view;
+ }
+}
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/utilities/AbstractDatePicker.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/utilities/AbstractDatePicker.java
new file mode 100644
index 0000000..b51bbbc
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/utilities/AbstractDatePicker.java
@@ -0,0 +1,39 @@
+package ru.spbau.group202.notdeadbydeadline.ui.utilities;
+
+import android.app.DatePickerDialog;
+import android.app.Dialog;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+
+import org.jetbrains.annotations.NotNull;
+import org.joda.time.LocalDate;
+
+public abstract class AbstractDatePicker extends DialogFragment
+ implements DatePickerDialog.OnDateSetListener {
+ private int year;
+ private int month;
+ private int day;
+
+ public AbstractDatePicker() {
+
+ // Use the current date as the default date in the picker
+ LocalDate localDate = new LocalDate();
+ year = localDate.getYear();
+ month = localDate.getMonthOfYear() - 1;
+ day = localDate.getDayOfMonth();
+ }
+
+ public void setValues(int year, int month, int day) {
+ this.year = year;
+ this.month = month;
+ this.day = day;
+ }
+
+ @NotNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+
+ // Create a new instance of DatePickerDialog and return it
+ return new DatePickerDialog(getActivity(), this, year, month, day);
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/utilities/AbstractTimePicker.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/utilities/AbstractTimePicker.java
new file mode 100644
index 0000000..f9c9584
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/utilities/AbstractTimePicker.java
@@ -0,0 +1,39 @@
+package ru.spbau.group202.notdeadbydeadline.ui.utilities;
+
+import android.app.Dialog;
+import android.app.TimePickerDialog;
+import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
+import android.text.format.DateFormat;
+
+import org.jetbrains.annotations.NotNull;
+import org.joda.time.LocalTime;
+
+public abstract class AbstractTimePicker extends DialogFragment
+ implements TimePickerDialog.OnTimeSetListener {
+
+ private int hour;
+ private int minute;
+
+ public AbstractTimePicker() {
+ // Use the current time as the default values for the picker
+ LocalTime localTime = new LocalTime();
+ hour = localTime.getHourOfDay();
+ minute = localTime.getMinuteOfHour();
+ }
+
+ public void setValues(int hour, int minute) {
+ this.hour = hour;
+ this.minute = minute;
+ }
+
+ @NotNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+
+
+ // Create a new instance of TimePickerDialog and return it
+ return new TimePickerDialog(getActivity(), this, hour, minute,
+ DateFormat.is24HourFormat(getActivity()));
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/utilities/AttachmentsDialogFragment.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/utilities/AttachmentsDialogFragment.java
new file mode 100644
index 0000000..939a31a
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/utilities/AttachmentsDialogFragment.java
@@ -0,0 +1,99 @@
+package ru.spbau.group202.notdeadbydeadline.ui.utilities;
+
+import android.app.Dialog;
+import android.content.ActivityNotFoundException;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.OpenableColumns;
+import android.support.annotation.NonNull;
+import android.support.v4.app.DialogFragment;
+import android.support.v7.app.AlertDialog;
+import android.webkit.MimeTypeMap;
+import android.widget.Toast;
+
+import org.apache.commons.io.FilenameUtils;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+
+import static org.apache.commons.io.FileUtils.getFile;
+
+public class AttachmentsDialogFragment extends DialogFragment implements DialogInterface.OnClickListener {
+
+ private ArrayList filepaths = new ArrayList<>();
+
+ public void setFilepaths(ArrayList filepaths) {
+ this.filepaths = filepaths;
+ }
+
+ private String parseFilename(String filename) {
+ File f = new File(filename);
+ return f.getName();
+ }
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+
+ String[] paths = new String[filepaths.size()];
+ paths = filepaths.toArray(paths);
+ for (int i = 0; i < paths.length; i++) {
+ paths[i] = parseFilename(paths[i]);
+ }
+
+ builder.setTitle("Choose file to open")
+ .setItems(paths, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int which) {
+ openFile(filepaths.get(which));
+ Toast.makeText(getActivity(),
+ "Chosen file: " + filepaths.get(which),
+ Toast.LENGTH_SHORT).show();
+ }
+ });
+
+ return builder.create();
+ }
+
+ private void openFile(String filepath) {
+ Intent intent = new Intent();
+ intent.setAction(android.content.Intent.ACTION_VIEW);
+ File file = new File(filepath);
+
+ String extension = FilenameUtils.getExtension(filepath);
+ switch (extension) {
+ case "jpg":
+ case "png":
+ case "gif":
+ intent.setDataAndType(Uri.fromFile(file), "image/*");
+ break;
+ case "pdf":
+ intent.setDataAndType(Uri.fromFile(file), "application/pdf");
+ break;
+ case "txt":
+ intent.setDataAndType(Uri.fromFile(file), "text/plain");
+ break;
+ case "doc": // TODO does this even work
+ intent.setDataAndType(Uri.fromFile(file), "application/msword");
+ break;
+ case "docx": // TODO does this even work
+ intent.setDataAndType(Uri.fromFile(file), "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
+ break;
+ case "html":
+ intent.setDataAndType(Uri.fromFile(file), "text/html");
+ break;
+ }
+ startActivity(intent);
+ }
+
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+
+ }
+}
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/utilities/ListViewUtility.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/utilities/ListViewUtility.java
new file mode 100644
index 0000000..a4398a2
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/utilities/ListViewUtility.java
@@ -0,0 +1,39 @@
+package ru.spbau.group202.notdeadbydeadline.ui.utilities;
+
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+
+public class ListViewUtility {
+
+ // set listView2 height based on listView1 height
+ private static void setListViewHeightBasedOnChildren(ListView listView1,
+ ListView listView2) {
+ ListAdapter listAdapter = listView1.getAdapter();
+ if (listAdapter == null) {
+ // pre-condition
+ return;
+ }
+ int totalHeight = 0;
+ for (int i = 0; i < listAdapter.getCount(); i++) {
+ View listItem = listAdapter.getView(i, null, listView1);
+ listItem.measure(0, 0);
+ totalHeight += listItem.getMeasuredHeight();
+ }
+
+ ViewGroup.LayoutParams params = listView2.getLayoutParams();
+ params.height = totalHeight + (listView2.getDividerHeight() * (listAdapter.getCount() - 1));
+ listView2.setLayoutParams(params);
+ }
+
+ public static void setTwoListViewsHeight(ListView listView1, ListView listView2) {
+ if (listView1.getAdapter().getCount() > listView2.getAdapter().getCount()) {
+ setListViewHeightBasedOnChildren(listView1, listView2);
+ setListViewHeightBasedOnChildren(listView1, listView1);
+ } else {
+ setListViewHeightBasedOnChildren(listView2, listView2);
+ setListViewHeightBasedOnChildren(listView2, listView1);
+ }
+ }
+}
diff --git a/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/utilities/WeekDayEnum.java b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/utilities/WeekDayEnum.java
new file mode 100644
index 0000000..aeec4b7
--- /dev/null
+++ b/app/src/main/java/ru/spbau/group202/notdeadbydeadline/ui/utilities/WeekDayEnum.java
@@ -0,0 +1,5 @@
+package ru.spbau.group202.notdeadbydeadline.ui.utilities;
+
+public enum WeekDayEnum {
+ Monday, Tuesday, Wednesday, Thursday, Friday, Saturday;
+}
diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 0000000..c7bd21d
--- /dev/null
+++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/button_border.xml b/app/src/main/res/drawable/button_border.xml
new file mode 100644
index 0000000..3b82370
--- /dev/null
+++ b/app/src/main/res/drawable/button_border.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 0000000..d5fccc5
--- /dev/null
+++ b/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,170 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_note_add_white_24dp.xml b/app/src/main/res/drawable/ic_note_add_white_24dp.xml
new file mode 100644
index 0000000..750127b
--- /dev/null
+++ b/app/src/main/res/drawable/ic_note_add_white_24dp.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/drawable/side_nav_bar.xml b/app/src/main/res/drawable/side_nav_bar.xml
new file mode 100644
index 0000000..6d81870
--- /dev/null
+++ b/app/src/main/res/drawable/side_nav_bar.xml
@@ -0,0 +1,9 @@
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_add_homework.xml b/app/src/main/res/layout/activity_add_homework.xml
new file mode 100644
index 0000000..d3eb924
--- /dev/null
+++ b/app/src/main/res/layout/activity_add_homework.xml
@@ -0,0 +1,158 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_add_schedule_entry.xml b/app/src/main/res/layout/activity_add_schedule_entry.xml
new file mode 100644
index 0000000..0b293bd
--- /dev/null
+++ b/app/src/main/res/layout/activity_add_schedule_entry.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_deadlines.xml b/app/src/main/res/layout/activity_deadlines.xml
new file mode 100644
index 0000000..9feffbd
--- /dev/null
+++ b/app/src/main/res/layout/activity_deadlines.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_display_homework.xml b/app/src/main/res/layout/activity_display_homework.xml
new file mode 100644
index 0000000..976385f
--- /dev/null
+++ b/app/src/main/res/layout/activity_display_homework.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_homework.xml b/app/src/main/res/layout/activity_homework.xml
new file mode 100644
index 0000000..bb5661c
--- /dev/null
+++ b/app/src/main/res/layout/activity_homework.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..a61d8a6
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_schedule.xml b/app/src/main/res/layout/activity_schedule.xml
new file mode 100644
index 0000000..f0f95cb
--- /dev/null
+++ b/app/src/main/res/layout/activity_schedule.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/app_bar_deadlines.xml b/app/src/main/res/layout/app_bar_deadlines.xml
new file mode 100644
index 0000000..57d17c8
--- /dev/null
+++ b/app/src/main/res/layout/app_bar_deadlines.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/app_bar_homework.xml b/app/src/main/res/layout/app_bar_homework.xml
new file mode 100644
index 0000000..7d4ec2a
--- /dev/null
+++ b/app/src/main/res/layout/app_bar_homework.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/app_bar_main.xml b/app/src/main/res/layout/app_bar_main.xml
new file mode 100644
index 0000000..9cb8017
--- /dev/null
+++ b/app/src/main/res/layout/app_bar_main.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/app_bar_schedule.xml b/app/src/main/res/layout/app_bar_schedule.xml
new file mode 100644
index 0000000..955a268
--- /dev/null
+++ b/app/src/main/res/layout/app_bar_schedule.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/content_add_homework.xml b/app/src/main/res/layout/content_add_homework.xml
new file mode 100644
index 0000000..983f217
--- /dev/null
+++ b/app/src/main/res/layout/content_add_homework.xml
@@ -0,0 +1,11 @@
+
+
+
+
diff --git a/app/src/main/res/layout/content_add_schedule_entry.xml b/app/src/main/res/layout/content_add_schedule_entry.xml
new file mode 100644
index 0000000..6b10be8
--- /dev/null
+++ b/app/src/main/res/layout/content_add_schedule_entry.xml
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/content_deadlines.xml b/app/src/main/res/layout/content_deadlines.xml
new file mode 100644
index 0000000..1ac3cc7
--- /dev/null
+++ b/app/src/main/res/layout/content_deadlines.xml
@@ -0,0 +1,290 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/content_display_homework.xml b/app/src/main/res/layout/content_display_homework.xml
new file mode 100644
index 0000000..067bfa8
--- /dev/null
+++ b/app/src/main/res/layout/content_display_homework.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
diff --git a/app/src/main/res/layout/content_homework.xml b/app/src/main/res/layout/content_homework.xml
new file mode 100644
index 0000000..dea456e
--- /dev/null
+++ b/app/src/main/res/layout/content_homework.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml
new file mode 100644
index 0000000..8b06ca7
--- /dev/null
+++ b/app/src/main/res/layout/content_main.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/content_schedule.xml b/app/src/main/res/layout/content_schedule.xml
new file mode 100644
index 0000000..d6293f9
--- /dev/null
+++ b/app/src/main/res/layout/content_schedule.xml
@@ -0,0 +1,235 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/custom_deadline_listview_entry.xml b/app/src/main/res/layout/custom_deadline_listview_entry.xml
new file mode 100644
index 0000000..1ceee68
--- /dev/null
+++ b/app/src/main/res/layout/custom_deadline_listview_entry.xml
@@ -0,0 +1,14 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/custom_homework_listview_entry.xml b/app/src/main/res/layout/custom_homework_listview_entry.xml
new file mode 100644
index 0000000..ce0834a
--- /dev/null
+++ b/app/src/main/res/layout/custom_homework_listview_entry.xml
@@ -0,0 +1,13 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/custom_homework_listview_item.xml b/app/src/main/res/layout/custom_homework_listview_item.xml
new file mode 100644
index 0000000..cc35467
--- /dev/null
+++ b/app/src/main/res/layout/custom_homework_listview_item.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/custom_mainscreen_listview_entry.xml b/app/src/main/res/layout/custom_mainscreen_listview_entry.xml
new file mode 100644
index 0000000..91fe8b6
--- /dev/null
+++ b/app/src/main/res/layout/custom_mainscreen_listview_entry.xml
@@ -0,0 +1,13 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/custom_schedule_listview_item.xml b/app/src/main/res/layout/custom_schedule_listview_item.xml
new file mode 100644
index 0000000..812463a
--- /dev/null
+++ b/app/src/main/res/layout/custom_schedule_listview_item.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/custom_textview_for_bigger_listview.xml b/app/src/main/res/layout/custom_textview_for_bigger_listview.xml
new file mode 100644
index 0000000..503aaa8
--- /dev/null
+++ b/app/src/main/res/layout/custom_textview_for_bigger_listview.xml
@@ -0,0 +1,14 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/nav_header_deadlines.xml b/app/src/main/res/layout/nav_header_deadlines.xml
new file mode 100644
index 0000000..7c9ef39
--- /dev/null
+++ b/app/src/main/res/layout/nav_header_deadlines.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/nav_header_homework.xml b/app/src/main/res/layout/nav_header_homework.xml
new file mode 100644
index 0000000..869f896
--- /dev/null
+++ b/app/src/main/res/layout/nav_header_homework.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/nav_header_main.xml b/app/src/main/res/layout/nav_header_main.xml
new file mode 100644
index 0000000..6403883
--- /dev/null
+++ b/app/src/main/res/layout/nav_header_main.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/nav_header_schedule.xml b/app/src/main/res/layout/nav_header_schedule.xml
new file mode 100644
index 0000000..7c9ef39
--- /dev/null
+++ b/app/src/main/res/layout/nav_header_schedule.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/menu/activity_deadlines_drawer.xml b/app/src/main/res/menu/activity_deadlines_drawer.xml
new file mode 100644
index 0000000..c5c9010
--- /dev/null
+++ b/app/src/main/res/menu/activity_deadlines_drawer.xml
@@ -0,0 +1,24 @@
+
+
diff --git a/app/src/main/res/menu/activity_homework_drawer.xml b/app/src/main/res/menu/activity_homework_drawer.xml
new file mode 100644
index 0000000..9259ac9
--- /dev/null
+++ b/app/src/main/res/menu/activity_homework_drawer.xml
@@ -0,0 +1,25 @@
+
+
diff --git a/app/src/main/res/menu/activity_main_drawer.xml b/app/src/main/res/menu/activity_main_drawer.xml
new file mode 100644
index 0000000..9259ac9
--- /dev/null
+++ b/app/src/main/res/menu/activity_main_drawer.xml
@@ -0,0 +1,25 @@
+
+
diff --git a/app/src/main/res/menu/activity_schedule_drawer.xml b/app/src/main/res/menu/activity_schedule_drawer.xml
new file mode 100644
index 0000000..9259ac9
--- /dev/null
+++ b/app/src/main/res/menu/activity_schedule_drawer.xml
@@ -0,0 +1,25 @@
+
+
diff --git a/app/src/main/res/menu/deadlines.xml b/app/src/main/res/menu/deadlines.xml
new file mode 100644
index 0000000..a2411e3
--- /dev/null
+++ b/app/src/main/res/menu/deadlines.xml
@@ -0,0 +1,9 @@
+
+
diff --git a/app/src/main/res/menu/homework.xml b/app/src/main/res/menu/homework.xml
new file mode 100644
index 0000000..a2411e3
--- /dev/null
+++ b/app/src/main/res/menu/homework.xml
@@ -0,0 +1,9 @@
+
+
diff --git a/app/src/main/res/menu/homework_listview_item_menu.xml b/app/src/main/res/menu/homework_listview_item_menu.xml
new file mode 100644
index 0000000..a26887b
--- /dev/null
+++ b/app/src/main/res/menu/homework_listview_item_menu.xml
@@ -0,0 +1,15 @@
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/main.xml b/app/src/main/res/menu/main.xml
new file mode 100644
index 0000000..a2411e3
--- /dev/null
+++ b/app/src/main/res/menu/main.xml
@@ -0,0 +1,9 @@
+
+
diff --git a/app/src/main/res/menu/schedule.xml b/app/src/main/res/menu/schedule.xml
new file mode 100644
index 0000000..a2411e3
--- /dev/null
+++ b/app/src/main/res/menu/schedule.xml
@@ -0,0 +1,9 @@
+
+
diff --git a/app/src/main/res/menu/schedule_listview_item_menu.xml b/app/src/main/res/menu/schedule_listview_item_menu.xml
new file mode 100644
index 0000000..480a25a
--- /dev/null
+++ b/app/src/main/res/menu/schedule_listview_item_menu.xml
@@ -0,0 +1,11 @@
+
\ No newline at end of file
diff --git a/app/src/main/res/menu/week_day_menu.xml b/app/src/main/res/menu/week_day_menu.xml
new file mode 100644
index 0000000..2832d49
--- /dev/null
+++ b/app/src/main/res/menu/week_day_menu.xml
@@ -0,0 +1,27 @@
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 0000000..eca70cf
--- /dev/null
+++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..1b52399
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-hdpi/ndbd.png b/app/src/main/res/mipmap-hdpi/ndbd.png
new file mode 100644
index 0000000..148cba1
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ndbd.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..115a4c7
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ndbd.png b/app/src/main/res/mipmap-mdpi/ndbd.png
new file mode 100644
index 0000000..6b773d1
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ndbd.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..459ca60
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ndbd.png b/app/src/main/res/mipmap-xhdpi/ndbd.png
new file mode 100644
index 0000000..5de91e2
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ndbd.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..8e19b41
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ndbd.png b/app/src/main/res/mipmap-xxhdpi/ndbd.png
new file mode 100644
index 0000000..001a181
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ndbd.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..4c19a13
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ndbd.png b/app/src/main/res/mipmap-xxxhdpi/ndbd.png
new file mode 100644
index 0000000..8786180
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ndbd.png differ
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..c20e4ca
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #c5efed
+
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..4ab4520
--- /dev/null
+++ b/app/src/main/res/values/dimens.xml
@@ -0,0 +1,8 @@
+
+
+ 16dp
+ 16dp
+ 8dp
+ 176dp
+ 16dp
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..132ae83
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,76 @@
+
+ Not Dead By Deadline
+ Deadlines
+ Not Dead By Deadline
+
+ Open navigation drawer
+ Close navigation drawer
+
+ Settings
+
+ Deadlines
+ Homework
+ Schedule
+ Study Materials
+
+ DeadlinesActivity
+ HomeworkActivity
+ AddHomeworkActivity
+
+ Add a new homework entry
+ Subject
+
+ Set due date
+ Set due time
+ Description
+ Expected Score
+ Regular
+ Finish
+ How to submit
+ DisplayHomeworkActivity
+ StudyMaterialsActivity
+ DeadlineActivity
+ ScheduleActivity
+ Home
+
+ Monday, %s
+ Thursday, %s
+ Tuesday, %s
+ Friday, %s
+ Wednesday, %s
+ Saturday, %s
+ Sunday, %s
+
+ Monday
+ Thursday
+ Tuesday
+ Friday
+ Wednesday
+ Saturday
+ Sunday
+
+ Add new h/w entry
+
+ Next week
+ Previous week
+ Schedule
+ AddScheduleEntryActivity
+ Add a new schedule entry
+ Auditorium
+ Teacher
+ Set time
+ Set week day
+ is on even week
+ Edit
+ Delete
+
+ Edit this h/w entry
+ Edit this schedule entry
+ Regularity:
+ weeks
+ 0
+ Attach files
+ Open attachment
+ Not specified
+
+
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..545b9c6
--- /dev/null
+++ b/app/src/main/res/values/styles.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/xml/backup_descriptor.xml b/app/src/main/res/xml/backup_descriptor.xml
new file mode 100644
index 0000000..2c63b62
--- /dev/null
+++ b/app/src/main/res/xml/backup_descriptor.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/test/java/ru/spbau/group202/notdeadbydeadline/ExampleUnitTest.java b/app/src/test/java/ru/spbau/group202/notdeadbydeadline/ExampleUnitTest.java
new file mode 100644
index 0000000..d6162c3
--- /dev/null
+++ b/app/src/test/java/ru/spbau/group202/notdeadbydeadline/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package ru.spbau.group202.notdeadbydeadline;
+
+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/build.gradle b/build.gradle
new file mode 100644
index 0000000..e6b32bc
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,27 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.0.1'
+
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..ad602b5
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Tue Nov 07 23:09:09 MSK 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..9d82f78
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/java_project.iml b/java_project.iml
new file mode 100644
index 0000000..dae9575
--- /dev/null
+++ b/java_project.iml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..8aa6663
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,2 @@
+include ':app'
+include ':aFileChooser'
\ No newline at end of file