commit 5c65209f2bd5b53a82fae0bf310adc94e19eea84 Author: aNNiMON Date: Wed Feb 14 00:01:58 2024 +0200 Initial commit diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..5176974 --- /dev/null +++ b/.classpath @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/.project b/.project new file mode 100644 index 0000000..803c16e --- /dev/null +++ b/.project @@ -0,0 +1,33 @@ + + + Journal + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/AndroidManifest.xml b/AndroidManifest.xml new file mode 100644 index 0000000..ae8be26 --- /dev/null +++ b/AndroidManifest.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/proguard-project.txt b/proguard-project.txt new file mode 100644 index 0000000..f2fe155 --- /dev/null +++ b/proguard-project.txt @@ -0,0 +1,20 @@ +# To enable ProGuard in your project, edit project.properties +# to define the proguard.config property as described in that file. +# +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in ${sdk.dir}/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/project.properties b/project.properties new file mode 100644 index 0000000..e999993 --- /dev/null +++ b/project.properties @@ -0,0 +1,16 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +# Project target. +target=android-19 +android.library.reference.1=..\\TableFixHeaders +android.library.reference.2=../android-support-v7-appcompat diff --git a/res/drawable-hdpi/drawer_shadow.9.png b/res/drawable-hdpi/drawer_shadow.9.png new file mode 100644 index 0000000..aebc557 Binary files /dev/null and b/res/drawable-hdpi/drawer_shadow.9.png differ diff --git a/res/drawable-hdpi/ic_action_export.png b/res/drawable-hdpi/ic_action_export.png new file mode 100644 index 0000000..549316a Binary files /dev/null and b/res/drawable-hdpi/ic_action_export.png differ diff --git a/res/drawable-hdpi/ic_action_new.png b/res/drawable-hdpi/ic_action_new.png new file mode 100644 index 0000000..95eb9ea Binary files /dev/null and b/res/drawable-hdpi/ic_action_new.png differ diff --git a/res/drawable-hdpi/ic_drawer.png b/res/drawable-hdpi/ic_drawer.png new file mode 100644 index 0000000..b508ed9 Binary files /dev/null and b/res/drawable-hdpi/ic_drawer.png differ diff --git a/res/drawable-hdpi/ic_launcher.png b/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000..221eab4 Binary files /dev/null and b/res/drawable-hdpi/ic_launcher.png differ diff --git a/res/drawable-mdpi/drawer_shadow.9.png b/res/drawable-mdpi/drawer_shadow.9.png new file mode 100644 index 0000000..4725380 Binary files /dev/null and b/res/drawable-mdpi/drawer_shadow.9.png differ diff --git a/res/drawable-mdpi/ic_action_export.png b/res/drawable-mdpi/ic_action_export.png new file mode 100644 index 0000000..dbb6c08 Binary files /dev/null and b/res/drawable-mdpi/ic_action_export.png differ diff --git a/res/drawable-mdpi/ic_action_new.png b/res/drawable-mdpi/ic_action_new.png new file mode 100644 index 0000000..51205c6 Binary files /dev/null and b/res/drawable-mdpi/ic_action_new.png differ diff --git a/res/drawable-mdpi/ic_drawer.png b/res/drawable-mdpi/ic_drawer.png new file mode 100644 index 0000000..08eaf50 Binary files /dev/null and b/res/drawable-mdpi/ic_drawer.png differ diff --git a/res/drawable-mdpi/ic_launcher.png b/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000..f7a57a8 Binary files /dev/null and b/res/drawable-mdpi/ic_launcher.png differ diff --git a/res/drawable-xhdpi/drawer_shadow.9.png b/res/drawable-xhdpi/drawer_shadow.9.png new file mode 100644 index 0000000..0fe08f4 Binary files /dev/null and b/res/drawable-xhdpi/drawer_shadow.9.png differ diff --git a/res/drawable-xhdpi/ic_action_export.png b/res/drawable-xhdpi/ic_action_export.png new file mode 100644 index 0000000..624c7c1 Binary files /dev/null and b/res/drawable-xhdpi/ic_action_export.png differ diff --git a/res/drawable-xhdpi/ic_action_new.png b/res/drawable-xhdpi/ic_action_new.png new file mode 100644 index 0000000..c1e34aa Binary files /dev/null and b/res/drawable-xhdpi/ic_action_new.png differ diff --git a/res/drawable-xhdpi/ic_drawer.png b/res/drawable-xhdpi/ic_drawer.png new file mode 100644 index 0000000..639e3c0 Binary files /dev/null and b/res/drawable-xhdpi/ic_drawer.png differ diff --git a/res/drawable-xhdpi/ic_launcher.png b/res/drawable-xhdpi/ic_launcher.png new file mode 100644 index 0000000..154a61e Binary files /dev/null and b/res/drawable-xhdpi/ic_launcher.png differ diff --git a/res/drawable-xxhdpi/drawer_shadow.9.png b/res/drawable-xxhdpi/drawer_shadow.9.png new file mode 100644 index 0000000..bfeb2db Binary files /dev/null and b/res/drawable-xxhdpi/drawer_shadow.9.png differ diff --git a/res/drawable-xxhdpi/ic_action_new.png b/res/drawable-xxhdpi/ic_action_new.png new file mode 100644 index 0000000..a446a6a Binary files /dev/null and b/res/drawable-xxhdpi/ic_action_new.png differ diff --git a/res/drawable-xxhdpi/ic_drawer.png b/res/drawable-xxhdpi/ic_drawer.png new file mode 100644 index 0000000..c2b2215 Binary files /dev/null and b/res/drawable-xxhdpi/ic_drawer.png differ diff --git a/res/drawable-xxhdpi/ic_launcher.png b/res/drawable-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..e99eb22 Binary files /dev/null and b/res/drawable-xxhdpi/ic_launcher.png differ diff --git a/res/drawable/ab_background.xml b/res/drawable/ab_background.xml new file mode 100644 index 0000000..b511be3 --- /dev/null +++ b/res/drawable/ab_background.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/res/drawable/bg_table_header.xml b/res/drawable/bg_table_header.xml new file mode 100644 index 0000000..6380354 --- /dev/null +++ b/res/drawable/bg_table_header.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/res/drawable/bg_table_item1.xml b/res/drawable/bg_table_item1.xml new file mode 100644 index 0000000..716ea90 --- /dev/null +++ b/res/drawable/bg_table_item1.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/res/drawable/bg_table_item2.xml b/res/drawable/bg_table_item2.xml new file mode 100644 index 0000000..4996e20 --- /dev/null +++ b/res/drawable/bg_table_item2.xml @@ -0,0 +1,7 @@ + + + + + + diff --git a/res/drawable/list_selector.xml b/res/drawable/list_selector.xml new file mode 100644 index 0000000..ebe1937 --- /dev/null +++ b/res/drawable/list_selector.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/res/layout-sw600dp-land/activity_main.xml b/res/layout-sw600dp-land/activity_main.xml new file mode 100644 index 0000000..b525ea7 --- /dev/null +++ b/res/layout-sw600dp-land/activity_main.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + diff --git a/res/layout/activity_main.xml b/res/layout/activity_main.xml new file mode 100644 index 0000000..fb5fa5c --- /dev/null +++ b/res/layout/activity_main.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + diff --git a/res/layout/dialog_add_criteria.xml b/res/layout/dialog_add_criteria.xml new file mode 100644 index 0000000..97dfabd --- /dev/null +++ b/res/layout/dialog_add_criteria.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/dialog_add_group.xml b/res/layout/dialog_add_group.xml new file mode 100644 index 0000000..9c01d13 --- /dev/null +++ b/res/layout/dialog_add_group.xml @@ -0,0 +1,19 @@ + + + + + + + + + \ No newline at end of file diff --git a/res/layout/dialog_add_mark.xml b/res/layout/dialog_add_mark.xml new file mode 100644 index 0000000..071be8a --- /dev/null +++ b/res/layout/dialog_add_mark.xml @@ -0,0 +1,19 @@ + + + + + + + + + \ No newline at end of file diff --git a/res/layout/dialog_add_student.xml b/res/layout/dialog_add_student.xml new file mode 100644 index 0000000..47eed17 --- /dev/null +++ b/res/layout/dialog_add_student.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/dialog_add_subject.xml b/res/layout/dialog_add_subject.xml new file mode 100644 index 0000000..9588edf --- /dev/null +++ b/res/layout/dialog_add_subject.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/fragment_main.xml b/res/layout/fragment_main.xml new file mode 100644 index 0000000..171ea55 --- /dev/null +++ b/res/layout/fragment_main.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/res/layout/fragment_navigation_drawer.xml b/res/layout/fragment_navigation_drawer.xml new file mode 100644 index 0000000..af78aac --- /dev/null +++ b/res/layout/fragment_navigation_drawer.xml @@ -0,0 +1,9 @@ + \ No newline at end of file diff --git a/res/layout/fragment_table.xml b/res/layout/fragment_table.xml new file mode 100644 index 0000000..302441b --- /dev/null +++ b/res/layout/fragment_table.xml @@ -0,0 +1,17 @@ + + + + + + + + + \ No newline at end of file diff --git a/res/layout/item_nav_drawer.xml b/res/layout/item_nav_drawer.xml new file mode 100644 index 0000000..05747f7 --- /dev/null +++ b/res/layout/item_nav_drawer.xml @@ -0,0 +1,10 @@ + + \ No newline at end of file diff --git a/res/menu/add_item.xml b/res/menu/add_item.xml new file mode 100644 index 0000000..74f9fa0 --- /dev/null +++ b/res/menu/add_item.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/res/menu/item_context.xml b/res/menu/item_context.xml new file mode 100644 index 0000000..de18de3 --- /dev/null +++ b/res/menu/item_context.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/res/menu/main.xml b/res/menu/main.xml new file mode 100644 index 0000000..4f483dd --- /dev/null +++ b/res/menu/main.xml @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/res/values-v11/styles.xml b/res/values-v11/styles.xml new file mode 100644 index 0000000..5b7fc04 --- /dev/null +++ b/res/values-v11/styles.xml @@ -0,0 +1,19 @@ + + + + + + + + diff --git a/res/values/colors.xml b/res/values/colors.xml new file mode 100644 index 0000000..1301d34 --- /dev/null +++ b/res/values/colors.xml @@ -0,0 +1,9 @@ + + + #E2F3E4 + #51BB5B + #3FA348 + + #C4E6C8 + #89D191 + diff --git a/res/values/dimens.xml b/res/values/dimens.xml new file mode 100644 index 0000000..d7a42fb --- /dev/null +++ b/res/values/dimens.xml @@ -0,0 +1,9 @@ + + + + 240dp + + diff --git a/res/values/strings.xml b/res/values/strings.xml new file mode 100644 index 0000000..673337c --- /dev/null +++ b/res/values/strings.xml @@ -0,0 +1,36 @@ + + + + Журнал преподавателя + Open navigation drawer + Close navigation drawer + Действие + Настройки + Добавить + Экспорт + + Разрешён только один аккаунт + Пожалуйста, подождите… + Нет данных для отображения + Ошибка.\nПроверьте правильность ввода + Сохранено + + Название + Сокращение + Фамилия + Имя + Отчество + Дата (yyyy-MM-dd) + Оценка или текст + + Добавить + Удалить + + + Журнал + Дисциплины + Критерии оценки + Группы + Студенты + + diff --git a/res/values/styles.xml b/res/values/styles.xml new file mode 100644 index 0000000..e2a136e --- /dev/null +++ b/res/values/styles.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + diff --git a/res/xml/authenticator.xml b/res/xml/authenticator.xml new file mode 100644 index 0000000..85730c5 --- /dev/null +++ b/res/xml/authenticator.xml @@ -0,0 +1,6 @@ + + \ No newline at end of file diff --git a/res/xml/db_sync.xml b/res/xml/db_sync.xml new file mode 100644 index 0000000..d0c8ce7 --- /dev/null +++ b/res/xml/db_sync.xml @@ -0,0 +1,8 @@ + + \ No newline at end of file diff --git a/src/com/university/journal/EmptyTableAdapter.java b/src/com/university/journal/EmptyTableAdapter.java new file mode 100644 index 0000000..8fb2864 --- /dev/null +++ b/src/com/university/journal/EmptyTableAdapter.java @@ -0,0 +1,66 @@ +package com.university.journal; + +import android.content.Context; +import android.database.DataSetObserver; +import android.view.View; +import android.view.ViewGroup; +import com.inqbarna.tablefixheaders.adapters.TableAdapter; + +/** + * Empty adapter for marks table. + */ +public final class EmptyTableAdapter implements TableAdapter { + + private final Context mContext; + + public EmptyTableAdapter(Context context) { + this.mContext = context; + } + + @Override + public void registerDataSetObserver(DataSetObserver observer) { + } + + @Override + public void unregisterDataSetObserver(DataSetObserver observer) { + } + + @Override + public int getRowCount() { + return 1; + } + + @Override + public int getColumnCount() { + return 1; + } + + @Override + public View getView(int row, int column, View convertView, ViewGroup parent) { + if (convertView == null) { + convertView = new View(mContext); + } + return convertView; + } + + @Override + public int getWidth(int column) { + return 200; + } + + @Override + public int getHeight(int row) { + return 100; + } + + @Override + public int getItemViewType(int row, int column) { + return 0; + } + + @Override + public int getViewTypeCount() { + return 1; + } + +} diff --git a/src/com/university/journal/MainActivity.java b/src/com/university/journal/MainActivity.java new file mode 100644 index 0000000..ebf60ee --- /dev/null +++ b/src/com/university/journal/MainActivity.java @@ -0,0 +1,104 @@ +package com.university.journal; + +import static com.university.journal.accounts.Authenticator.ACCOUNT_TYPE; +import android.accounts.Account; +import android.accounts.AccountManager; +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.widget.DrawerLayout; +import android.support.v7.app.ActionBar; +import android.support.v7.app.ActionBarActivity; +import com.university.journal.accounts.Authenticator; +import com.university.journal.fragments.CriteriaFragment; +import com.university.journal.fragments.GroupFragment; +import com.university.journal.fragments.MainFragment; +import com.university.journal.fragments.StudentFragment; +import com.university.journal.fragments.SubjectFragment; + +public final class MainActivity extends ActionBarActivity + implements NavigationDrawerFragment.NavigationDrawerCallbacks { + + /** + * Fragment managing the behaviors, interactions and presentation of the + * navigation drawer. + */ + private NavigationDrawerFragment mNavigationDrawerFragment; + + /** + * Used to store the last screen title. For use in + * {@link #restoreActionBar()}. + */ + private CharSequence mTitle; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + final AccountManager accountManager = AccountManager.get(this); + final Account[] accounts = accountManager.getAccountsByType(ACCOUNT_TYPE); + if (accounts.length == 0) { + final String login = getString(R.string.app_name); + Authenticator.addAccount(login, accountManager); + } + + setContentView(R.layout.activity_main); + + mNavigationDrawerFragment = (NavigationDrawerFragment) getSupportFragmentManager() + .findFragmentById(R.id.navigation_drawer); + mTitle = getTitle(); + + if (mNavigationDrawerFragment != null) { + // Set up the drawer. + mNavigationDrawerFragment.setUp(R.id.navigation_drawer, + (DrawerLayout) findViewById(R.id.drawer_layout)); + } + } + + @Override + public void onNavigationDrawerItemSelected(int position) { + // update the main content by replacing fragments + Fragment frag = null; + + switch (position) { + case 0: // + frag = new MainFragment(); + break; + case 1: // + frag = new SubjectFragment(); + break; + case 2: // + frag = new CriteriaFragment(); + break; + case 3: // + frag = new GroupFragment(); + break; + case 4: // + frag = new StudentFragment(); + break; + } + + if (frag == null) return; + + onSectionAttached(position); + + FragmentManager fragmentManager = getSupportFragmentManager(); + fragmentManager + .beginTransaction() + .replace(R.id.container, frag).commit(); + } + + private void onSectionAttached(int number) { + final String[] titles = getResources().getStringArray(R.array.nav_sections); + mTitle = titles[number]; + restoreActionBar(); + } + + private void restoreActionBar() { + ActionBar actionBar = getSupportActionBar(); + if (actionBar == null) return; + actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); + actionBar.setDisplayShowTitleEnabled(true); + actionBar.setTitle(mTitle); + } +} diff --git a/src/com/university/journal/MarksTableAdapter.java b/src/com/university/journal/MarksTableAdapter.java new file mode 100644 index 0000000..2b7890f --- /dev/null +++ b/src/com/university/journal/MarksTableAdapter.java @@ -0,0 +1,117 @@ +package com.university.journal; + +import android.content.Context; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.TextView; +import com.inqbarna.tablefixheaders.adapters.BaseTableAdapter; +import com.university.journal.entity.Criteria; +import com.university.journal.entity.MarksTableItem; + +public final class MarksTableAdapter extends BaseTableAdapter { + + private static final int VIEW_TYPE_HEADER = 0, VIEW_TYPE_MARK = 1; + + private final Context mContext; + private final Criteria[] mHeaders; + private final MarksTableItem[] mItems; + private final int mColumnHeaderHeight, mRowHeaderWidth, mWidth, mHeight; + + private View.OnClickListener mMarkClickListener; + + public MarksTableAdapter(Context context, Criteria[] headers, MarksTableItem[] items) { + mContext = context; + mHeaders = headers; + mItems = items; + + DisplayMetrics dm = context.getResources().getDisplayMetrics(); + mRowHeaderWidth = Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 125, dm)); + mWidth = Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 100, dm)); + mColumnHeaderHeight = Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 60, dm)); + mHeight = Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 40, dm)); + } + + public Criteria getHeader(int column) { + return mHeaders[column]; + } + + public MarksTableItem getItem(int row) { + return mItems[row]; + } + + public void setMarkClickListener(View.OnClickListener markClickListener) { + mMarkClickListener = markClickListener; + } + + @Override + public int getRowCount() { + return mItems.length; + } + + @Override + public int getColumnCount() { + return mHeaders.length; + } + + @Override + public View getView(int row, int column, View convertView, ViewGroup parent) { + switch (getItemViewType(row, column)) { + case VIEW_TYPE_HEADER: + if (convertView == null) { + convertView = new TextView(mContext); + convertView.setBackgroundResource(R.drawable.bg_table_header); + } + if (row == -1 && column == -1) break; + if (row == -1) { + // Column header - Criteries + ((TextView) convertView).setText(mHeaders[column].readableString()); + ((TextView) convertView).setGravity(Gravity.CENTER_HORIZONTAL); + } else { + // Row header - Students + ((TextView) convertView).setText(mItems[row].getStudent().readableString()); + ((TextView) convertView).setGravity(Gravity.CENTER_VERTICAL); + } + break; + case VIEW_TYPE_MARK: + if (convertView == null) { + convertView = new Button(mContext); + convertView.setOnClickListener(mMarkClickListener); + } + final Button btn = (Button) convertView; + btn.setText(mItems[row].getMark(column).getMark()); + + final int resId = (row % 2 == 0) ? + R.drawable.bg_table_item1 : R.drawable.bg_table_item2; + convertView.setBackgroundResource(resId); + break; + } + + return convertView; + } + + @Override + public int getWidth(int column) { + return (column == -1) ? mRowHeaderWidth : mWidth; + } + + @Override + public int getHeight(int row) { + return (row == -1) ? mColumnHeaderHeight : mHeight; + } + + @Override + public int getItemViewType(int row, int column) { + if (row == -1 || column == -1) return VIEW_TYPE_HEADER; + return VIEW_TYPE_MARK; + } + + @Override + public int getViewTypeCount() { + return 2; + } + +} diff --git a/src/com/university/journal/NavigationDrawerFragment.java b/src/com/university/journal/NavigationDrawerFragment.java new file mode 100644 index 0000000..94e6f5c --- /dev/null +++ b/src/com/university/journal/NavigationDrawerFragment.java @@ -0,0 +1,230 @@ +package com.university.journal; + +import android.app.Activity; +import android.content.SharedPreferences; +import android.content.res.Configuration; +import android.os.Bundle; +import android.preference.PreferenceManager; +import android.support.v4.app.ActionBarDrawerToggle; +import android.support.v4.app.Fragment; +import android.support.v4.view.GravityCompat; +import android.support.v4.widget.DrawerLayout; +import android.support.v7.app.ActionBar; +import android.support.v7.app.ActionBarActivity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.ListView; + +/** + * Fragment used for managing interactions for and presentation of a navigation drawer. + * See the + * design guidelines for a complete explanation of the behaviors implemented here. + */ +public final class NavigationDrawerFragment extends Fragment { + + /** + * Remember the position of the selected item. + */ + private static final String STATE_SELECTED_POSITION = "selected_navigation_drawer_position"; + + /** + * Per the design guidelines, you should show the drawer on launch until the user manually + * expands it. This shared preference tracks this. + */ + private static final String PREF_USER_LEARNED_DRAWER = "navigation_drawer_learned"; + + /** + * A pointer to the current callbacks instance (the Activity). + */ + private NavigationDrawerCallbacks mCallbacks; + + /** + * Helper component that ties the action bar to the navigation drawer. + */ + private ActionBarDrawerToggle mDrawerToggle; + + private DrawerLayout mDrawerLayout; + private ListView mDrawerListView; + private View mFragmentContainerView; + + private int mCurrentSelectedPosition = 0; + private boolean mFromSavedInstanceState; + private boolean mUserLearnedDrawer; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Read in the flag indicating whether or not the user has demonstrated awareness of the + // drawer. See PREF_USER_LEARNED_DRAWER for details. + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity()); + mUserLearnedDrawer = sp.getBoolean(PREF_USER_LEARNED_DRAWER, false); + + if (savedInstanceState != null) { + mCurrentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION); + mFromSavedInstanceState = true; + } + + // Select either the default item (0) or the last selected item. + selectItem(mCurrentSelectedPosition); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + mDrawerListView = (ListView) inflater.inflate( + R.layout.fragment_navigation_drawer, container, false); + mDrawerListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + selectItem(position); + } + }); + mDrawerListView.setAdapter(new ArrayAdapter<>( + getActionBar().getThemedContext(), + R.layout.item_nav_drawer, + android.R.id.text1, + getResources().getStringArray(R.array.nav_sections))); + mDrawerListView.setItemChecked(mCurrentSelectedPosition, true); + return mDrawerListView; + } + + public boolean isDrawerOpen() { + return mDrawerLayout != null && mDrawerLayout.isDrawerOpen(mFragmentContainerView); + } + + /** + * Users of this fragment must call this method to set up the navigation drawer interactions. + * + * @param fragmentId The android:id of this fragment in its activity's layout. + * @param drawerLayout The DrawerLayout containing this fragment's UI. + */ + public void setUp(int fragmentId, DrawerLayout drawerLayout) { + mFragmentContainerView = getActivity().findViewById(fragmentId); + mDrawerLayout = drawerLayout; + if (mDrawerLayout == null) return; + + // set a custom shadow that overlays the main content when the drawer opens + mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START); + // set up the drawer's list view with items and click listener + + ActionBar actionBar = getActionBar(); + actionBar.setDisplayHomeAsUpEnabled(true); + actionBar.setHomeButtonEnabled(true); + + // ActionBarDrawerToggle ties together the the proper interactions + // between the navigation drawer and the action bar app icon. + + mDrawerToggle = new ActionBarDrawerToggle( + getActivity(), /* host Activity */ + mDrawerLayout, /* DrawerLayout object */ + R.drawable.ic_drawer, /* nav drawer image to replace 'Up' caret */ + R.string.navigation_drawer_open, /* "open drawer" description for accessibility */ + R.string.navigation_drawer_close /* "close drawer" description for accessibility */ + ) { + @Override + public void onDrawerClosed(View drawerView) { + super.onDrawerClosed(drawerView); + if (!isAdded()) { + return; + } + + getActivity().supportInvalidateOptionsMenu(); // calls onPrepareOptionsMenu() + } + + @Override + public void onDrawerOpened(View drawerView) { + super.onDrawerOpened(drawerView); + if (!isAdded()) { + return; + } + + if (!mUserLearnedDrawer) { + // The user manually opened the drawer; store this flag to prevent auto-showing + // the navigation drawer automatically in the future. + mUserLearnedDrawer = true; + SharedPreferences sp = PreferenceManager + .getDefaultSharedPreferences(getActivity()); + sp.edit().putBoolean(PREF_USER_LEARNED_DRAWER, true).commit(); + } + + getActivity().supportInvalidateOptionsMenu(); // calls onPrepareOptionsMenu() + } + }; + + // If the user hasn't 'learned' about the drawer, open it to introduce them to the drawer, + // per the navigation drawer design guidelines. + if (!mUserLearnedDrawer && !mFromSavedInstanceState) { + mDrawerLayout.openDrawer(mFragmentContainerView); + } + + // Defer code dependent on restoration of previous instance state. + mDrawerLayout.post(new Runnable() { + @Override + public void run() { + mDrawerToggle.syncState(); + } + }); + + mDrawerLayout.setDrawerListener(mDrawerToggle); + } + + private void selectItem(int position) { + mCurrentSelectedPosition = position; + if (mDrawerListView != null) { + mDrawerListView.setItemChecked(position, true); + } + if (mDrawerLayout != null) { + mDrawerLayout.closeDrawer(mFragmentContainerView); + } + if (mCallbacks != null) { + mCallbacks.onNavigationDrawerItemSelected(position); + } + } + + @Override + public void onAttach(Activity activity) { + super.onAttach(activity); + try { + mCallbacks = (NavigationDrawerCallbacks) activity; + } catch (ClassCastException e) { + throw new ClassCastException("Activity must implement NavigationDrawerCallbacks."); + } + } + + @Override + public void onDetach() { + super.onDetach(); + mCallbacks = null; + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt(STATE_SELECTED_POSITION, mCurrentSelectedPosition); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + // Forward the new configuration the drawer toggle component. + mDrawerToggle.onConfigurationChanged(newConfig); + } + + private ActionBar getActionBar() { + return ((ActionBarActivity) getActivity()).getSupportActionBar(); + } + + /** + * Callbacks interface that all activities using this fragment must implement. + */ + public static interface NavigationDrawerCallbacks { + /** + * Called when an item in the navigation drawer is selected. + */ + void onNavigationDrawerItemSelected(int position); + } +} diff --git a/src/com/university/journal/accounts/AccountService.java b/src/com/university/journal/accounts/AccountService.java new file mode 100644 index 0000000..404c754 --- /dev/null +++ b/src/com/university/journal/accounts/AccountService.java @@ -0,0 +1,20 @@ +package com.university.journal.accounts; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; + +public final class AccountService extends Service { + + private Authenticator mAuthenticator; + + @Override + public void onCreate() { + mAuthenticator = new Authenticator(this); + } + + @Override + public IBinder onBind(Intent intent) { + return mAuthenticator.getIBinder(); + } +} diff --git a/src/com/university/journal/accounts/Authenticator.java b/src/com/university/journal/accounts/Authenticator.java new file mode 100644 index 0000000..f031b27 --- /dev/null +++ b/src/com/university/journal/accounts/Authenticator.java @@ -0,0 +1,110 @@ +package com.university.journal.accounts; + +import static com.university.journal.db.DbContract.AUTHORITY; +import com.university.journal.R; +import android.accounts.AbstractAccountAuthenticator; +import android.accounts.Account; +import android.accounts.AccountAuthenticatorResponse; +import android.accounts.AccountManager; +import android.accounts.NetworkErrorException; +import android.content.ContentResolver; +import android.content.Context; +import android.os.Bundle; + +public final class Authenticator extends AbstractAccountAuthenticator { + + private static final long SYNC_PERIOD = 24 * 60 * 60; + + public static final String AUTH_TOKEN_TYPE = "FULL"; + public static final String ACCOUNT_TYPE = "com.university.journal"; + + public static void addAccount(String login, AccountManager accountManager) { + final Account account = new Account(login, ACCOUNT_TYPE); + + accountManager.addAccountExplicitly(account, "password", null); + accountManager.setAuthToken(account, AUTH_TOKEN_TYPE, "token"); + + ContentResolver.setIsSyncable(account, AUTHORITY, 1); + ContentResolver.setSyncAutomatically(account, AUTHORITY, true); + ContentResolver.addPeriodicSync(account, AUTHORITY, new Bundle(), SYNC_PERIOD); + // Force sync + Bundle extras = new Bundle(); + extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true); + extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true); + ContentResolver.requestSync(account, AUTHORITY, extras); + } + + private final Context mContext; + + public Authenticator(Context context) { + super(context); + this.mContext = context; + } + + @Override + public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) + throws NetworkErrorException { + AccountManager accountManager = AccountManager.get(mContext); + Account[] accountsByType = accountManager.getAccountsByType(accountType); + final Bundle bundle = new Bundle(); + // Allow only one account + if (accountsByType.length > 0) { + bundle.putString(AccountManager.KEY_ERROR_MESSAGE, mContext.getString(R.string.one_account_allowed)); + return bundle; + } + + return null; + } + + @Override + public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) + throws NetworkErrorException { + return null; + } + + @Override + public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) { + return null; + } + + @Override + public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) + throws NetworkErrorException { + final Bundle bundle = new Bundle(); + if (!authTokenType.equals(AUTH_TOKEN_TYPE)) { + bundle.putString(AccountManager.KEY_ERROR_MESSAGE, "Invalid authTokenType"); + return bundle; + } + + final AccountManager am = AccountManager.get(mContext); + final String password = am.getPassword(account); + if (password != null) { + bundle.putString(AccountManager.KEY_ACCOUNT_NAME, account.name); + bundle.putString(AccountManager.KEY_ACCOUNT_TYPE, ACCOUNT_TYPE); + bundle.putString(AccountManager.KEY_AUTHTOKEN, account.name + "/" + password); + return bundle; + } + + return null; + } + + @Override + public String getAuthTokenLabel(String authTokenType) { + if (authTokenType.equals(AUTH_TOKEN_TYPE)) { + return mContext.getString(R.string.app_name); + } + return null; + } + + @Override + public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) + throws NetworkErrorException { + return null; + } + + @Override + public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) + throws NetworkErrorException { + return null; + } +} diff --git a/src/com/university/journal/db/ContentManager.java b/src/com/university/journal/db/ContentManager.java new file mode 100644 index 0000000..664762b --- /dev/null +++ b/src/com/university/journal/db/ContentManager.java @@ -0,0 +1,83 @@ +package com.university.journal.db; + +import java.util.ArrayList; +import java.util.List; +import android.content.ContentResolver; +import android.database.Cursor; +import com.university.journal.db.DbContract.Criteries; +import com.university.journal.db.DbContract.Marks; +import com.university.journal.db.DbContract.Students; +import com.university.journal.entity.Criteria; +import com.university.journal.entity.Mark; +import com.university.journal.entity.Student; + +public final class ContentManager { + + public static synchronized ContentManager with(ContentResolver cr) { + return new ContentManager(cr); + } + + private final ContentResolver mResolver; + + private ContentManager(ContentResolver resolver) { + this.mResolver = resolver; + } + + public List getCriteries(long subjectId) { + final String selection = Criteries.SUBJECT_ID + " = ?"; + final String[] args = { String.valueOf(subjectId) }; + Cursor cursor = mResolver.query( + Criteries.CONTENT_URI, Criteries.PROJECTION_ALL, + selection, args, Criteries.DATE); + List criteries = new ArrayList<>(); + if (cursor != null) { + while (cursor.moveToNext()) { + Criteria obj = new Criteria(); + obj.fromCursor(cursor); + criteries.add(obj); + } + } + if (cursor != null) { + cursor.close(); + } + return criteries; + } + + public List getStudents(long groupId) { + final String selection = Students.GROUP_ID + " = ?"; + final String[] args = { String.valueOf(groupId) }; + Cursor cursor = mResolver.query(Students.CONTENT_URI, null, + selection, args, Students.LAST_NAME); + ArrayList students = new ArrayList<>(); + if (cursor != null) { + while (cursor.moveToNext()) { + Student obj = new Student(); + obj.fromCursor(cursor); + obj.setId( cursor.getInt(cursor.getColumnIndex(Students._ID)) ); + students.add(obj); + } + } + if (cursor != null) { + cursor.close(); + } + return students; + } + + public Cursor getMarksCursor(long studentId) { + final String sel = Marks.STUDENT_ID + " = ?"; + final String[] args = { String.valueOf(studentId) }; + return mResolver.query(Marks.CONTENT_URI, null, sel, args, Marks.CRITERIA_ID); + } + + public void insertOrUpdate(Mark mark) { + final String where = String.format("%s = ? AND %s = ?", Marks.CRITERIA_ID, Marks.STUDENT_ID); + final String[] args = { + String.valueOf(mark.getCriteriaId()), + String.valueOf(mark.getStudentId()) + }; + int updateCount = mResolver.update(Marks.CONTENT_URI, mark.getContentValues(), where, args); + if (updateCount <= 0) { + mResolver.insert(Marks.CONTENT_URI, mark.getContentValues()); + } + } +} diff --git a/src/com/university/journal/db/DbContentProvider.java b/src/com/university/journal/db/DbContentProvider.java new file mode 100644 index 0000000..91c486a --- /dev/null +++ b/src/com/university/journal/db/DbContentProvider.java @@ -0,0 +1,313 @@ +package com.university.journal.db; + +import static com.university.journal.db.DbContract.*; +import android.content.ContentProvider; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.UriMatcher; +import android.database.Cursor; +import android.database.SQLException; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteQueryBuilder; +import android.net.Uri; +import android.text.TextUtils; + +public final class DbContentProvider extends ContentProvider { + + private static final UriMatcher URI_MATCHER = buildUriMatcher(); + private static final int STUDENTS_LIST = 10; + private static final int STUDENTS_ID = 11; + private static final int SUBJECTS_LIST = 20; + private static final int SUBJECTS_ID = 21; + private static final int GROUPS_LIST = 30; + private static final int GROUPS_ID = 31; + private static final int CRITERIES_LIST = 40; + private static final int CRITERIES_ID = 41; + private static final int MARKS_LIST = 50; + private static final int MARKS_ID = 51; + private static final int GROUP_BY_STUDENT = 91; + + private DbOpenHelper mDbHelper; + + private static final UriMatcher buildUriMatcher() { + final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); + matcher.addURI(DbContract.AUTHORITY, Students.TABLE_NAME, STUDENTS_LIST); + matcher.addURI(DbContract.AUTHORITY, Students.TABLE_NAME + "/#", STUDENTS_ID); + matcher.addURI(DbContract.AUTHORITY, Subjects.TABLE_NAME, SUBJECTS_LIST); + matcher.addURI(DbContract.AUTHORITY, Subjects.TABLE_NAME + "/#", SUBJECTS_ID); + matcher.addURI(DbContract.AUTHORITY, Groups.TABLE_NAME, GROUPS_LIST); + matcher.addURI(DbContract.AUTHORITY, Groups.TABLE_NAME + "/#", GROUPS_ID); + matcher.addURI(DbContract.AUTHORITY, Criteries.TABLE_NAME, CRITERIES_LIST); + matcher.addURI(DbContract.AUTHORITY, Criteries.TABLE_NAME + "/#", CRITERIES_ID); + matcher.addURI(DbContract.AUTHORITY, Marks.TABLE_NAME, MARKS_LIST); + matcher.addURI(DbContract.AUTHORITY, Marks.TABLE_NAME + "/#", MARKS_ID); + matcher.addURI(DbContract.AUTHORITY, "group_by_student", GROUP_BY_STUDENT); + return matcher; + } + + @Override + public boolean onCreate() { + mDbHelper = new DbOpenHelper(getContext()); + return true; + } + + @Override + public String getType(Uri uri) { + final int match = URI_MATCHER.match(uri); + switch (match) { + case STUDENTS_LIST: + return Students.CONTENT_TYPE; + case STUDENTS_ID: + return Students.CONTENT_ITEM_TYPE; + case SUBJECTS_LIST: + return Subjects.CONTENT_TYPE; + case SUBJECTS_ID: + return Subjects.CONTENT_ITEM_TYPE; + case GROUPS_LIST: + return Groups.CONTENT_TYPE; + case GROUPS_ID: + return Groups.CONTENT_ITEM_TYPE; + case CRITERIES_LIST: + return Criteries.CONTENT_TYPE; + case CRITERIES_ID: + return Criteries.CONTENT_ITEM_TYPE; + case MARKS_LIST: + return Marks.CONTENT_TYPE; + case MARKS_ID: + return Marks.CONTENT_ITEM_TYPE; + default: + throw new IllegalArgumentException("URI " + uri + " is not supported."); + } + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + SQLiteDatabase db = mDbHelper.getReadableDatabase(); + SQLiteQueryBuilder builder = new SQLiteQueryBuilder(); + final int match = URI_MATCHER.match(uri); + switch (match) { + case STUDENTS_LIST: + builder.setTables(Students.TABLE_NAME); + break; + case STUDENTS_ID: + builder.setTables(Students.TABLE_NAME); + builder.appendWhere(Students._ID + " = " + uri.getLastPathSegment()); + break; + case SUBJECTS_LIST: + builder.setTables(Subjects.TABLE_NAME); + break; + case SUBJECTS_ID: + builder.setTables(Subjects.TABLE_NAME); + builder.appendWhere(Subjects._ID + " = " + uri.getLastPathSegment()); + break; + case GROUPS_LIST: + builder.setTables(Groups.TABLE_NAME); + break; + case GROUPS_ID: + builder.setTables(Groups.TABLE_NAME); + builder.appendWhere(Groups._ID + " = " + uri.getLastPathSegment()); + break; + case CRITERIES_LIST: + builder.setTables(Criteries.TABLE_NAME); + break; + case CRITERIES_ID: + builder.setTables(Criteries.TABLE_NAME); + builder.appendWhere(Criteries._ID + " = " + uri.getLastPathSegment()); + break; + case MARKS_LIST: + builder.setTables(Marks.TABLE_NAME); + break; + case MARKS_ID: + builder.setTables(Marks.TABLE_NAME); + builder.appendWhere(Marks._ID + " = " + uri.getLastPathSegment()); + break; + case GROUP_BY_STUDENT: + builder.setTables(String.format( + "%s INNER JOIN %s ON %s.%s = %s.%s", + Students.TABLE_NAME, Groups.TABLE_NAME, + Students.TABLE_NAME, Students.GROUP_ID, + Groups.TABLE_NAME, Groups._ID)); + projection = new String[] { + Groups.NAME, Students.GROUP_ID + }; + break; + default: + throw new IllegalArgumentException("URI " + uri + " is not supported."); + } + return builder.query(db, projection, selection, selectionArgs, null, null, sortOrder); + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + SQLiteDatabase db = mDbHelper.getWritableDatabase(); + final int match = URI_MATCHER.match(uri); + switch(match){ + case STUDENTS_LIST: + long id = db.insert(Students.TABLE_NAME, null, values); + return getInsertedUri(id, uri); + case SUBJECTS_LIST: + id = db.insert(Subjects.TABLE_NAME, null, values); + return getInsertedUri(id, uri); + case GROUPS_LIST: + id = db.insert(Groups.TABLE_NAME, null, values); + return getInsertedUri(id, uri); + case CRITERIES_LIST: + id = db.insert(Criteries.TABLE_NAME, null, values); + return getInsertedUri(id, uri); + case MARKS_LIST: + id = db.insert(Marks.TABLE_NAME, null, values); + return getInsertedUri(id, uri); + default: + throw new IllegalArgumentException("URI " + uri + " is not supported."); + } + } + + private Uri getInsertedUri(long id, Uri uri) { + if (id <= 0) throw new SQLException("Problem while inserting into uri: " + uri); + Uri itemUri = ContentUris.withAppendedId(uri, id); + getContext().getContentResolver().notifyChange(itemUri, null); + return itemUri; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + SQLiteDatabase db = mDbHelper.getWritableDatabase(); + int updateCount = 0; + final int match = URI_MATCHER.match(uri); + switch (match) { + case STUDENTS_LIST: + updateCount = db.update(Students.TABLE_NAME, values, selection, selectionArgs); + break; + case STUDENTS_ID: + String where = Students._ID + " = " + uri.getLastPathSegment(); + if (!TextUtils.isEmpty(selection)) { + where += " AND " + selection; + } + updateCount = db.update(Students.TABLE_NAME, values, where, selectionArgs); + break; + + case SUBJECTS_LIST: + updateCount = db.update(Subjects.TABLE_NAME, values, selection, selectionArgs); + break; + case SUBJECTS_ID: + where = Subjects._ID + " = " + uri.getLastPathSegment(); + if (!TextUtils.isEmpty(selection)) { + where += " AND " + selection; + } + updateCount = db.update(Subjects.TABLE_NAME, values, where, selectionArgs); + break; + + case GROUPS_LIST: + updateCount = db.update(Groups.TABLE_NAME, values, selection, selectionArgs); + break; + case GROUPS_ID: + where = Groups._ID + " = " + uri.getLastPathSegment(); + if (!TextUtils.isEmpty(selection)) { + where += " AND " + selection; + } + updateCount = db.update(Groups.TABLE_NAME, values, where, selectionArgs); + break; + + case CRITERIES_LIST: + updateCount = db.update(Criteries.TABLE_NAME, values, selection, selectionArgs); + break; + case CRITERIES_ID: + where = Criteries._ID + " = " + uri.getLastPathSegment(); + if (!TextUtils.isEmpty(selection)) { + where += " AND " + selection; + } + updateCount = db.update(Criteries.TABLE_NAME, values, where, selectionArgs); + break; + + case MARKS_LIST: + updateCount = db.update(Marks.TABLE_NAME, values, selection, selectionArgs); + break; + case MARKS_ID: + where = Marks._ID + " = " + uri.getLastPathSegment(); + if (!TextUtils.isEmpty(selection)) { + where += " AND " + selection; + } + updateCount = db.update(Marks.TABLE_NAME, values, where, selectionArgs); + break; + + default: + throw new IllegalArgumentException("URI " + uri + " is not supported."); + } + if (updateCount > 0) { + getContext().getContentResolver().notifyChange(uri, null); + } + return updateCount; + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + SQLiteDatabase db = mDbHelper.getWritableDatabase(); + int deleteCount = 0; + final int match = URI_MATCHER.match(uri); + switch (match) { + case STUDENTS_LIST: + deleteCount = db.delete(Students.TABLE_NAME, selection, selectionArgs); + break; + case STUDENTS_ID: + String where = Students._ID + " = " + uri.getLastPathSegment(); + if (!TextUtils.isEmpty(selection)) { + where += " AND " + selection; + } + deleteCount = db.delete(Students.TABLE_NAME, where, selectionArgs); + break; + + case SUBJECTS_LIST: + deleteCount = db.delete(Subjects.TABLE_NAME, selection, selectionArgs); + break; + case SUBJECTS_ID: + where = Subjects._ID + " = " + uri.getLastPathSegment(); + if (!TextUtils.isEmpty(selection)) { + where += " AND " + selection; + } + deleteCount = db.delete(Subjects.TABLE_NAME, where, selectionArgs); + break; + + case GROUPS_LIST: + deleteCount = db.delete(Groups.TABLE_NAME, selection, selectionArgs); + break; + case GROUPS_ID: + where = Groups._ID + " = " + uri.getLastPathSegment(); + if (!TextUtils.isEmpty(selection)) { + where += " AND " + selection; + } + deleteCount = db.delete(Groups.TABLE_NAME, where, selectionArgs); + break; + + case CRITERIES_LIST: + deleteCount = db.delete(Criteries.TABLE_NAME, selection, selectionArgs); + break; + case CRITERIES_ID: + where = Criteries._ID + " = " + uri.getLastPathSegment(); + if (!TextUtils.isEmpty(selection)) { + where += " AND " + selection; + } + deleteCount = db.delete(Criteries.TABLE_NAME, where, selectionArgs); + break; + + case MARKS_LIST: + deleteCount = db.delete(Marks.TABLE_NAME, selection, selectionArgs); + break; + case MARKS_ID: + where = Marks._ID + " = " + uri.getLastPathSegment(); + if (!TextUtils.isEmpty(selection)) { + where += " AND " + selection; + } + deleteCount = db.delete(Marks.TABLE_NAME, where, selectionArgs); + break; + + default: + throw new IllegalArgumentException("URI " + uri + " is not supported."); + } + if (deleteCount > 0) { + getContext().getContentResolver().notifyChange(uri, null); + } + return deleteCount; + } + +} + diff --git a/src/com/university/journal/db/DbContract.java b/src/com/university/journal/db/DbContract.java new file mode 100644 index 0000000..0425254 --- /dev/null +++ b/src/com/university/journal/db/DbContract.java @@ -0,0 +1,103 @@ +package com.university.journal.db; + +import android.content.ContentResolver; +import android.net.Uri; +import android.provider.BaseColumns; + +public final class DbContract { + + public static final String AUTHORITY = "com.university.journal.dbprovider"; + + public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY); + + public static final class Students implements BaseColumns { + + public static final String TABLE_NAME = "students"; + + public static final Uri CONTENT_URI = Uri.withAppendedPath(DbContract.CONTENT_URI, TABLE_NAME); + + public static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/vnd.com.university.journal." + TABLE_NAME; + + public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE + "/vnd.com.university.journal." + TABLE_NAME; + + public static final String LAST_NAME = "lastname"; + + public static final String FIRST_NAME = "firstname"; + + public static final String MIDDLE_NAME = "midname"; + + public static final String GROUP_ID = "group_id"; + + public static final String[] PROJECTION_ALL = {_ID, LAST_NAME, FIRST_NAME, MIDDLE_NAME, GROUP_ID}; + } + + public static final class Subjects implements BaseColumns { + + public static final String TABLE_NAME = "subjects"; + + public static final Uri CONTENT_URI = Uri.withAppendedPath(DbContract.CONTENT_URI, TABLE_NAME); + + public static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/vnd.com.university.journal." + TABLE_NAME; + + public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE + "/vnd.com.university.journal." + TABLE_NAME; + + public static final String NAME = "_name"; + + public static final String SHORT_NAME = "shortname"; + + public static final String[] PROJECTION_ALL = {_ID, NAME, SHORT_NAME}; + } + + public static final class Groups implements BaseColumns { + + public static final String TABLE_NAME = "_groups"; + + public static final Uri CONTENT_URI = Uri.withAppendedPath(DbContract.CONTENT_URI, TABLE_NAME); + + public static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/vnd.com.university.journal." + TABLE_NAME; + + public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE + "/vnd.com.university.journal." + TABLE_NAME; + + public static final String NAME = "_name"; + + public static final String[] PROJECTION_ALL = {_ID, NAME}; + } + + public static final class Criteries implements BaseColumns { + + public static final String TABLE_NAME = "criteries"; + + public static final Uri CONTENT_URI = Uri.withAppendedPath(DbContract.CONTENT_URI, TABLE_NAME); + + public static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/vnd.com.university.journal." + TABLE_NAME; + + public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE + "/vnd.com.university.journal." + TABLE_NAME; + + public static final String SUBJECT_ID = "subject_id"; + + public static final String NAME = "_name"; + + public static final String DATE = "_date"; + + public static final String[] PROJECTION_ALL = {_ID, SUBJECT_ID, NAME, DATE}; + } + + public static final class Marks implements BaseColumns { + + public static final String TABLE_NAME = "marks"; + + public static final Uri CONTENT_URI = Uri.withAppendedPath(DbContract.CONTENT_URI, TABLE_NAME); + + public static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/vnd.com.university.journal." + TABLE_NAME; + + public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE + "/vnd.com.university.journal." + TABLE_NAME; + + public static final String STUDENT_ID = "student_id"; + + public static final String CRITERIA_ID = "criteria_id"; + + public static final String MARK = "mark"; + + public static final String[] PROJECTION_ALL = {_ID, STUDENT_ID, CRITERIA_ID, MARK}; + } +} diff --git a/src/com/university/journal/db/DbOpenHelper.java b/src/com/university/journal/db/DbOpenHelper.java new file mode 100644 index 0000000..2ae59d8 --- /dev/null +++ b/src/com/university/journal/db/DbOpenHelper.java @@ -0,0 +1,42 @@ +package com.university.journal.db; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; + +public final class DbOpenHelper extends SQLiteOpenHelper { + + private static final String NAME = DbSchema.DB_NAME; + private static final int VERSION = 1; + + public DbOpenHelper(Context context) { + super(context, NAME, null, VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL(DbSchema.DDL_CREATE_TBL_STUDENTS); + db.execSQL(DbSchema.DDL_CREATE_TBL_SUBJECTS); + db.execSQL(DbSchema.DDL_CREATE_TBL_GROUPS); + db.execSQL(DbSchema.DDL_CREATE_TBL_CRITERIES); + db.execSQL(DbSchema.DDL_CREATE_TBL_MARKS); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + db.execSQL(DbSchema.DDL_DROP_TBL_STUDENTS); + db.execSQL(DbSchema.DDL_DROP_TBL_SUBJECTS); + db.execSQL(DbSchema.DDL_DROP_TBL_GROUPS); + db.execSQL(DbSchema.DDL_DROP_TBL_CRITERIES); + db.execSQL(DbSchema.DDL_DROP_TBL_MARKS); + onCreate(db); + } + + @Override + public void onOpen(SQLiteDatabase db) { + super.onOpen(db); + if (!db.isReadOnly()) { + db.execSQL("PRAGMA foreign_keys=ON;"); + } + } +} diff --git a/src/com/university/journal/db/DbSchema.java b/src/com/university/journal/db/DbSchema.java new file mode 100644 index 0000000..72ee214 --- /dev/null +++ b/src/com/university/journal/db/DbSchema.java @@ -0,0 +1,56 @@ +package com.university.journal.db; + +import static com.university.journal.db.DbContract.*; + +public interface DbSchema { + + String DB_NAME = "journal.db"; + + String DDL_CREATE_TBL_STUDENTS = + "CREATE TABLE " + Students.TABLE_NAME + " (" + + Students._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, \n" + + Students.LAST_NAME + " TEXT NOT NULL, \n" + + Students.FIRST_NAME + " TEXT NOT NULL, \n" + + Students.MIDDLE_NAME + " TEXT NOT NULL, \n" + + Students.GROUP_ID + " INTEGER NOT NULL, \n" + + "FOREIGN KEY("+ Students.GROUP_ID + ") REFERENCES " + Groups.TABLE_NAME + "(" + Groups._ID + ") ON DELETE CASCADE \n" + + ")"; + + String DDL_CREATE_TBL_SUBJECTS = + "CREATE TABLE " + Subjects.TABLE_NAME + " (" + + Subjects._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, \n" + + Subjects.NAME + " TEXT NOT NULL, \n" + + Subjects.SHORT_NAME + " TEXT NOT NULL \n" + + ")"; + + String DDL_CREATE_TBL_GROUPS = + "CREATE TABLE " + Groups.TABLE_NAME + " (" + + Groups._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, \n" + + Groups.NAME + " TEXT NOT NULL \n" + + ")"; + + String DDL_CREATE_TBL_CRITERIES = + "CREATE TABLE " + Criteries.TABLE_NAME + " (" + + Criteries._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, \n" + + Criteries.SUBJECT_ID + " INTEGER NOT NULL, \n" + + Criteries.NAME + " TEXT NOT NULL, \n" + + Criteries.DATE + " TEXT NOT NULL, \n" + + "FOREIGN KEY("+ Criteries.SUBJECT_ID + ") REFERENCES " + Subjects.TABLE_NAME + "(" + Subjects._ID + ") ON DELETE CASCADE \n" + + ")"; + + String DDL_CREATE_TBL_MARKS = + "CREATE TABLE " + Marks.TABLE_NAME + " (" + + Marks._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, \n" + + Marks.STUDENT_ID + " INTEGER NOT NULL, \n" + + Marks.CRITERIA_ID + " INTEGER NOT NULL, \n" + + Marks.MARK + " TEXT NOT NULL, \n" + + "FOREIGN KEY("+ Marks.STUDENT_ID + ") REFERENCES " + Students.TABLE_NAME + "(" + Students._ID + ") ON DELETE CASCADE, \n" + + "FOREIGN KEY("+ Marks.CRITERIA_ID + ") REFERENCES " + Criteries.TABLE_NAME + "(" + Criteries._ID + ") ON DELETE CASCADE \n" + + ")"; + + String DDL_DROP_TBL_STUDENTS = "DROP TABLE IF EXISTS " + Students.TABLE_NAME; + String DDL_DROP_TBL_SUBJECTS = "DROP TABLE IF EXISTS " + Subjects.TABLE_NAME; + String DDL_DROP_TBL_GROUPS = "DROP TABLE IF EXISTS " + Groups.TABLE_NAME; + String DDL_DROP_TBL_CRITERIES = "DROP TABLE IF EXISTS " + Criteries.TABLE_NAME; + String DDL_DROP_TBL_MARKS = "DROP TABLE IF EXISTS " + Marks.TABLE_NAME; +} diff --git a/src/com/university/journal/entity/Criteria.java b/src/com/university/journal/entity/Criteria.java new file mode 100644 index 0000000..8843571 --- /dev/null +++ b/src/com/university/journal/entity/Criteria.java @@ -0,0 +1,77 @@ +package com.university.journal.entity; + +import com.university.journal.db.DbContract.Criteries; +import android.content.ContentValues; +import android.database.Cursor; +import android.provider.BaseColumns; + +public final class Criteria implements DatabaseSerializer { + + private int id; + private int subjectId; + private String name; + private String date; + + public Criteria() { } + + public Criteria(String name, String date, int subjectId) { + this.name = name; + this.date = date; + this.subjectId = subjectId; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public int getSubjectId() { + return subjectId; + } + + public void setSubjectId(int subjectId) { + this.subjectId = subjectId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDate() { + return date; + } + + public void setDate(String date) { + this.date = date; + } + + @Override + public void fromCursor(Cursor cursor) { + id = cursor.getInt(cursor.getColumnIndex(BaseColumns._ID)); + subjectId = cursor.getInt(cursor.getColumnIndex(Criteries.SUBJECT_ID)); + name = cursor.getString(cursor.getColumnIndex(Criteries.NAME)); + date = cursor.getString(cursor.getColumnIndex(Criteries.DATE)); + } + + @Override + public ContentValues getContentValues() { + ContentValues cv = new ContentValues(); + cv.put(Criteries.NAME, name); + cv.put(Criteries.DATE, date); + cv.put(Criteries.SUBJECT_ID, subjectId); + return cv; + } + + public String readableString() { + return name + "\n" + date; + } + + +} diff --git a/src/com/university/journal/entity/DatabaseSerializer.java b/src/com/university/journal/entity/DatabaseSerializer.java new file mode 100644 index 0000000..d3045db --- /dev/null +++ b/src/com/university/journal/entity/DatabaseSerializer.java @@ -0,0 +1,11 @@ +package com.university.journal.entity; + +import android.content.ContentValues; +import android.database.Cursor; + +public interface DatabaseSerializer { + + void fromCursor(Cursor cursor); + + ContentValues getContentValues(); +} diff --git a/src/com/university/journal/entity/Group.java b/src/com/university/journal/entity/Group.java new file mode 100644 index 0000000..92d66e9 --- /dev/null +++ b/src/com/university/journal/entity/Group.java @@ -0,0 +1,47 @@ +package com.university.journal.entity; + +import com.university.journal.db.DbContract.Groups; +import android.content.ContentValues; +import android.database.Cursor; +import android.provider.BaseColumns; + +public final class Group implements DatabaseSerializer { + + private int id; + private String name; + + public Group() { } + + public Group(String name) { + this.name = name; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public void fromCursor(Cursor cursor) { + id = cursor.getInt(cursor.getColumnIndex(BaseColumns._ID)); + name = cursor.getString(cursor.getColumnIndex(Groups.NAME)); + } + + @Override + public ContentValues getContentValues() { + ContentValues cv = new ContentValues(); + cv.put(Groups.NAME, name); + return cv; + } +} diff --git a/src/com/university/journal/entity/Mark.java b/src/com/university/journal/entity/Mark.java new file mode 100644 index 0000000..ae09f4b --- /dev/null +++ b/src/com/university/journal/entity/Mark.java @@ -0,0 +1,70 @@ +package com.university.journal.entity; + +import com.university.journal.db.DbContract.Marks; +import android.content.ContentValues; +import android.database.Cursor; +import android.provider.BaseColumns; + +public final class Mark implements DatabaseSerializer { + + private int id; + private int studentId, criteriaId; + private String mark; + + public Mark() { } + + public Mark(int studentId, int criteriaId, String mark) { + this.studentId = studentId; + this.criteriaId = criteriaId; + this.mark = mark; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public int getStudentId() { + return studentId; + } + + public void setStudentId(int studentId) { + this.studentId = studentId; + } + + public int getCriteriaId() { + return criteriaId; + } + + public void setCriteriaId(int criteriaId) { + this.criteriaId = criteriaId; + } + + public String getMark() { + return mark; + } + + public void setMark(String mark) { + this.mark = mark; + } + + @Override + public void fromCursor(Cursor cursor) { + id = cursor.getInt(cursor.getColumnIndex(BaseColumns._ID)); + studentId = cursor.getInt(cursor.getColumnIndex(Marks.STUDENT_ID)); + criteriaId = cursor.getInt(cursor.getColumnIndex(Marks.CRITERIA_ID)); + mark = cursor.getString(cursor.getColumnIndex(Marks.MARK)); + } + + @Override + public ContentValues getContentValues() { + ContentValues cv = new ContentValues(); + cv.put(Marks.STUDENT_ID, studentId); + cv.put(Marks.CRITERIA_ID, criteriaId); + cv.put(Marks.MARK, mark); + return cv; + } +} diff --git a/src/com/university/journal/entity/MarksTableItem.java b/src/com/university/journal/entity/MarksTableItem.java new file mode 100644 index 0000000..b18e158 --- /dev/null +++ b/src/com/university/journal/entity/MarksTableItem.java @@ -0,0 +1,24 @@ +package com.university.journal.entity; + +public final class MarksTableItem { + + private Student student; + private Mark[] marks; + + public MarksTableItem(Student student, Mark[] marks) { + this.student = student; + this.marks = marks; + } + + public Student getStudent() { + return student; + } + + public Mark getMark(int index) { + return marks[index]; + } + + public void setMark(int index, Mark mark) { + marks[index] = mark; + } +} \ No newline at end of file diff --git a/src/com/university/journal/entity/Student.java b/src/com/university/journal/entity/Student.java new file mode 100644 index 0000000..2c2ff1e --- /dev/null +++ b/src/com/university/journal/entity/Student.java @@ -0,0 +1,83 @@ +package com.university.journal.entity; + +import com.university.journal.db.DbContract.Students; +import android.content.ContentValues; +import android.database.Cursor; + +public final class Student implements DatabaseSerializer { + + private int id; + private String firstName, lastName, middleName; + private int groupId; + + public Student() { } + + public Student(String firstName, String lastName, String middleName, int groupId) { + this.firstName = firstName; + this.lastName = lastName; + this.middleName = middleName; + this.groupId = groupId; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getMiddleName() { + return middleName; + } + + public void setMiddleName(String middleName) { + this.middleName = middleName; + } + + public int getGroupId() { + return groupId; + } + + public void setGroupId(int groupId) { + this.groupId = groupId; + } + + @Override + public void fromCursor(Cursor cursor) { + firstName = cursor.getString(cursor.getColumnIndex(Students.FIRST_NAME)); + lastName = cursor.getString(cursor.getColumnIndex(Students.LAST_NAME)); + middleName = cursor.getString(cursor.getColumnIndex(Students.MIDDLE_NAME)); + groupId = cursor.getInt(cursor.getColumnIndex(Students.GROUP_ID)); + } + + @Override + public ContentValues getContentValues() { + ContentValues cv = new ContentValues(); + cv.put(Students.FIRST_NAME, firstName); + cv.put(Students.LAST_NAME, lastName); + cv.put(Students.MIDDLE_NAME, middleName); + cv.put(Students.GROUP_ID, groupId); + return cv; + } + + public String readableString() { + return String.format("%s %c.%c.", lastName, firstName.charAt(0), middleName.charAt(0)); + } +} \ No newline at end of file diff --git a/src/com/university/journal/entity/Subject.java b/src/com/university/journal/entity/Subject.java new file mode 100644 index 0000000..438b666 --- /dev/null +++ b/src/com/university/journal/entity/Subject.java @@ -0,0 +1,58 @@ +package com.university.journal.entity; + +import static com.university.journal.db.DbContract.Subjects; +import android.content.ContentValues; +import android.database.Cursor; +import android.provider.BaseColumns; + +public final class Subject implements DatabaseSerializer { + + private int id; + private String name, shortName; + + public Subject() {} + + public Subject(String name, String shortName) { + this.name = name; + this.shortName = shortName; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getShortName() { + return shortName; + } + + public void setShortName(String shortName) { + this.shortName = shortName; + } + + @Override + public void fromCursor(Cursor cursor) { + id = cursor.getInt(cursor.getColumnIndex(BaseColumns._ID)); + name = cursor.getString(cursor.getColumnIndex(Subjects.NAME)); + shortName = cursor.getString(cursor.getColumnIndex(Subjects.SHORT_NAME)); + } + + @Override + public ContentValues getContentValues() { + ContentValues cv = new ContentValues(); + cv.put(Subjects.NAME, name); + cv.put(Subjects.SHORT_NAME, shortName); + return cv; + } +} diff --git a/src/com/university/journal/fragments/CriteriaFragment.java b/src/com/university/journal/fragments/CriteriaFragment.java new file mode 100644 index 0000000..26b72c2 --- /dev/null +++ b/src/com/university/journal/fragments/CriteriaFragment.java @@ -0,0 +1,62 @@ +package com.university.journal.fragments; + +import android.content.ContentUris; +import android.database.Cursor; +import android.os.Bundle; +import android.support.v4.app.DialogFragment; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import com.university.journal.db.DbContract.Criteries; +import com.university.journal.entity.Criteria; + +public final class CriteriaFragment extends DbTableFragment { + + @Override + protected int loaderNumber() { + return 3; + } + + @Override + protected String[] getUIBindFrom() { + return new String[] { Criteries.NAME, Criteries.DATE }; + } + + @Override + protected int[] getUIBindTo() { + return new int[] { android.R.id.text1, android.R.id.text2 }; + } + + @Override + protected int getUIBindLayoutResource() { + return android.R.layout.simple_list_item_2; + } + + @Override + protected DialogFragment getAddDataDialogFragment() { + return NewCriteriaDialogFragment.newInstance(mListener); + } + + @Override + protected void onItemDelete(long id) { + getActivity().getContentResolver() + .delete(ContentUris.withAppendedId(Criteries.CONTENT_URI, id), null, null); + } + + @Override + public Loader onCreateLoader(int id, Bundle bundle) { + return new CursorLoader(getActivity(), + Criteries.CONTENT_URI, Criteries.PROJECTION_ALL, + null, null, null); + } + + private NewCriteriaDialogFragment.AddListener mListener = new NewCriteriaDialogFragment.AddListener() { + + @Override + public void onConfirm(String name, String date, long subjectId) { + Criteria obj = new Criteria(name, date, (int) subjectId); + getActivity().getContentResolver() + .insert(Criteries.CONTENT_URI, obj.getContentValues()); + getActivity().getSupportLoaderManager().getLoader(loaderNumber()).forceLoad(); + } + }; +} diff --git a/src/com/university/journal/fragments/DbTableFragment.java b/src/com/university/journal/fragments/DbTableFragment.java new file mode 100644 index 0000000..05be682 --- /dev/null +++ b/src/com/university/journal/fragments/DbTableFragment.java @@ -0,0 +1,146 @@ +package com.university.journal.fragments; + +import android.database.Cursor; +import android.os.Bundle; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.FragmentTransaction; +import android.support.v4.app.ListFragment; +import android.support.v4.app.LoaderManager.LoaderCallbacks; +import android.support.v4.content.Loader; +import android.support.v4.widget.SimpleCursorAdapter; +import android.support.v7.app.ActionBarActivity; +import android.support.v7.view.ActionMode; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.widget.AdapterView; +import com.university.journal.R; + +public abstract class DbTableFragment extends ListFragment implements LoaderCallbacks { + + private long mSelectedId; + private SimpleCursorAdapter mAdapter; + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + setHasOptionsMenu(true); + + final String[] from = getUIBindFrom(); + final int[] to = getUIBindTo(); + + mAdapter = new SimpleCursorAdapter(getActivity(), + getUIBindLayoutResource(), null, + from, to, + SimpleCursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER); + postInitAdapter(mAdapter); + setListAdapter(mAdapter); + setEmptyText(getString(R.string.no_data)); + + getListView().setOnItemLongClickListener(mItemLongClickListener); + getActivity().getSupportLoaderManager().initLoader(loaderNumber(), null, this); + } + + protected abstract String[] getUIBindFrom(); + + protected int[] getUIBindTo() { + return new int[] { android.R.id.text1 }; + } + + protected int getUIBindLayoutResource() { + return android.R.layout.simple_list_item_1; + } + + protected void postInitAdapter(SimpleCursorAdapter adapter) { } + + protected abstract int loaderNumber(); + + protected abstract DialogFragment getAddDataDialogFragment(); + + protected abstract void onItemDelete(long id); + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.add_item, menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_add: + FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction(); + getAddDataDialogFragment().show(ft, "dialog"); + return true; + } + return false; + } + + @Override + public void onLoadFinished(Loader loader, Cursor cursor) { + if (mAdapter != null) { + mAdapter.swapCursor(cursor); + } + if (isResumed()) { + setListShown(true); + } else { + setListShownNoAnimation(true); + } + } + + @Override + public void onLoaderReset(Loader loader) { + if (mAdapter != null) { + mAdapter.swapCursor(null); + } + } + + private final AdapterView.OnItemLongClickListener mItemLongClickListener = + new AdapterView.OnItemLongClickListener() { + + @Override + public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { + mSelectedId = id; + ((ActionBarActivity) getActivity()).startSupportActionMode(mActionCallback); + return true; + } + + }; + + private final ActionMode.Callback mActionCallback = new ActionMode.Callback() { + + @Override + public boolean onActionItemClicked(ActionMode actionMode, MenuItem menuItem) { + // Similar to menu handling in Activity.onOptionsItemSelected() + switch (menuItem.getItemId()) { + case R.id.action_delete: + // Some remove functionality + onItemDelete(mSelectedId); + getActivity().getSupportLoaderManager().getLoader(loaderNumber()).forceLoad(); + actionMode.finish(); + return true; + } + + return false; + } + + @Override + public boolean onCreateActionMode(ActionMode actionMode, Menu menu) { + // Inflate our menu from a resource file + actionMode.getMenuInflater().inflate(R.menu.item_context, menu); + + // Return true so that the action mode is shown + return true; + } + + @Override + public void onDestroyActionMode(ActionMode actionMode) { + // Allows you to be notified when the action mode is dismissed + } + + @Override + public boolean onPrepareActionMode(ActionMode actionMode, Menu menu) { + return false; + } + }; +} diff --git a/src/com/university/journal/fragments/GroupFragment.java b/src/com/university/journal/fragments/GroupFragment.java new file mode 100644 index 0000000..7a84363 --- /dev/null +++ b/src/com/university/journal/fragments/GroupFragment.java @@ -0,0 +1,51 @@ +package com.university.journal.fragments; + +import android.content.ContentUris; +import android.database.Cursor; +import android.os.Bundle; +import android.support.v4.app.DialogFragment; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import com.university.journal.db.DbContract.Groups; +import com.university.journal.entity.Group; + +public final class GroupFragment extends DbTableFragment { + + @Override + protected int loaderNumber() { + return 1; + } + + @Override + protected String[] getUIBindFrom() { + return new String[] { Groups.NAME }; + } + + @Override + protected DialogFragment getAddDataDialogFragment() { + return NewGroupDialogFragment.newInstance(mListener); + } + + @Override + protected void onItemDelete(long id) { + getActivity().getContentResolver() + .delete(ContentUris.withAppendedId(Groups.CONTENT_URI, id), null, null); + } + + @Override + public Loader onCreateLoader(int id, Bundle bundle) { + return new CursorLoader(getActivity(), + Groups.CONTENT_URI, Groups.PROJECTION_ALL, + null, null, null); + } + + private NewGroupDialogFragment.AddListener mListener = new NewGroupDialogFragment.AddListener() { + + @Override + public void onConfirm(String name) { + Group obj = new Group(name); + getActivity().getContentResolver().insert(Groups.CONTENT_URI, obj.getContentValues()); + getActivity().getSupportLoaderManager().getLoader(loaderNumber()).forceLoad(); + } + }; +} diff --git a/src/com/university/journal/fragments/MainFragment.java b/src/com/university/journal/fragments/MainFragment.java new file mode 100644 index 0000000..d13fc86 --- /dev/null +++ b/src/com/university/journal/fragments/MainFragment.java @@ -0,0 +1,244 @@ +package com.university.journal.fragments; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.nio.charset.Charset; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.List; +import android.database.Cursor; +import android.os.Bundle; +import android.os.Environment; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentTransaction; +import android.support.v4.widget.SimpleCursorAdapter; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.Spinner; +import android.widget.Toast; +import com.inqbarna.tablefixheaders.TableFixHeaders; +import com.inqbarna.tablefixheaders.adapters.TableAdapter; +import com.university.journal.EmptyTableAdapter; +import com.university.journal.MarksTableAdapter; +import com.university.journal.R; +import com.university.journal.db.ContentManager; +import com.university.journal.db.DbContract.Groups; +import com.university.journal.db.DbContract.Subjects; +import com.university.journal.entity.Criteria; +import com.university.journal.entity.Mark; +import com.university.journal.entity.MarksTableItem; +import com.university.journal.entity.Student; + +public final class MainFragment extends Fragment { + + private static TableAdapter sEmptyAdapter; + + private Spinner mSubjectSpinner, mGroupSpinner; + private TableFixHeaders mTable; + private MarksTableAdapter mAdapter; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.fragment_main, container, false); + setHasOptionsMenu(true); + + mSubjectSpinner = (Spinner) view.findViewById(R.id.subjectSpinner); + mGroupSpinner = (Spinner) view.findViewById(R.id.groupSpinner); + mTable = (TableFixHeaders) view.findViewById(R.id.table); + + Cursor cur = getActivity().getContentResolver() + .query(Subjects.CONTENT_URI, Subjects.PROJECTION_ALL, null, null, null); + String[] from = new String[] { Subjects.NAME }; + int[] to = new int[] { android.R.id.text1 }; + SimpleCursorAdapter adapter = new SimpleCursorAdapter(getActivity(), + android.R.layout.simple_spinner_item, cur, + from, to, SimpleCursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + mSubjectSpinner.setAdapter(adapter); + mSubjectSpinner.setOnItemSelectedListener(mSpinnerListener); + + cur = getActivity().getContentResolver() + .query(Groups.CONTENT_URI, Groups.PROJECTION_ALL, null, null, null); + from = new String[] { Groups.NAME }; + to = new int[] { android.R.id.text1 }; + adapter = new SimpleCursorAdapter(getActivity(), + android.R.layout.simple_spinner_item, cur, + from, to, SimpleCursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + mGroupSpinner.setAdapter(adapter); + mGroupSpinner.setOnItemSelectedListener(mSpinnerListener); + + sEmptyAdapter = new EmptyTableAdapter(getActivity()); + mTable.setAdapter(sEmptyAdapter); + return view; + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.main, menu); + super.onCreateOptionsMenu(menu, inflater); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.action_export: + export(); + return true; + } + return super.onOptionsItemSelected(item); + } + + private void export() { + if (mAdapter == null) return; + + final String SEP = ",", NEWLINE = "\r\n"; + StringBuilder csv = new StringBuilder(); + // For correctly open in Excel + csv.append("sep=").append(SEP).append(NEWLINE); + // Headers + csv.append("").append(SEP); + for (int i = 0; i < mAdapter.getColumnCount(); i++) { + Criteria cr = mAdapter.getHeader(i); + String header = cr.getName() + " / " + cr.getDate();// cr.readableString(); + csv.append(header).append(SEP); + } + csv.append(NEWLINE); + // Data + for (int i = 0; i < mAdapter.getRowCount(); i++) { + MarksTableItem item = mAdapter.getItem(i); + csv.append(item.getStudent().readableString()).append(SEP); + for (int j = 0; j < mAdapter.getColumnCount(); j++) { + csv.append(item.getMark(j).getMark()).append(SEP); + } + csv.append(NEWLINE); + } + + // Save to sdcard + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd_HH_mm_ss"); + String fullPath = Environment.getExternalStorageDirectory() + + File.separator + sdf.format(new Date()) + ".csv"; + try { + Charset charset = Charset.isSupported("CP1251") + ? Charset.forName("CP1251") : Charset.defaultCharset(); + FileOutputStream out = new FileOutputStream(new File(fullPath)); + OutputStreamWriter writer = new OutputStreamWriter(out, charset); + writer.write(csv.toString()); + writer.flush(); + writer.close(); + Toast.makeText(getActivity(), + getString(R.string.saved) + " " + fullPath, + Toast.LENGTH_SHORT).show(); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + + private void showTable() { + long subjectId = mSubjectSpinner.getSelectedItemId(); + if (subjectId == AdapterView.INVALID_ROW_ID) return; + + long groupId = mGroupSpinner.getSelectedItemId(); + if (groupId == AdapterView.INVALID_ROW_ID) return; + + // Get criteries + List criteries = ContentManager + .with(getActivity().getContentResolver()) + .getCriteries(subjectId); + if (criteries.isEmpty()) return; + final Criteria[] headers = criteries.toArray(new Criteria[0]); + + // Get students + List students = ContentManager + .with(getActivity().getContentResolver()) + .getStudents(groupId); + + // Get marks + MarksTableItem[] items = new MarksTableItem[students.size()]; + for (int i = 0; i < items.length; i++) { + final Student student = students.get(i); + final int studentId = student.getId(); + Mark[] marks = new Mark[headers.length]; + for (int j = 0; j < headers.length; j++) { + marks[j] = new Mark(studentId, headers[j].getId(), ""); + } + + Cursor cursor = ContentManager + .with(getActivity().getContentResolver()) + .getMarksCursor(studentId); + if (cursor != null) { + while (cursor.moveToNext()) { + Mark obj = new Mark(); + obj.fromCursor(cursor); + for (int j = 0; j < headers.length; j++) { + if (headers[j].getId() == obj.getId()) { + marks[j] = obj; + break; + } + } + } + } + items[i] = new MarksTableItem(student, marks); + } + mAdapter = new MarksTableAdapter(getActivity(), headers, items); + mAdapter.setMarkClickListener(mMarksButtonListener); + mTable.setAdapter(mAdapter); + } + + private final View.OnClickListener mMarksButtonListener = + new View.OnClickListener() { + + @Override + public void onClick(View v) { + final int column = (int) v.getTag(R.id.tag_column); + final int row = (int) v.getTag(R.id.tag_row); + + final Mark mark = mAdapter.getItem(row).getMark(column); + DialogFragment frag = NewMarkDialogFragment.newInstance(mAddListener, + mark.getCriteriaId(), mark.getStudentId(), mark.getMark(), + row, column); + + FragmentTransaction ft = getActivity().getSupportFragmentManager().beginTransaction(); + frag.show(ft, "dialog"); + } + }; + + private NewMarkDialogFragment.AddListener mAddListener = new NewMarkDialogFragment.AddListener() { + + @Override + public void onConfirm(int row, int column, Mark mark) { + ContentManager.with(getActivity().getContentResolver()) + .insertOrUpdate(mark); + mAdapter.getItem(row).setMark(column, mark); + mAdapter.notifyDataSetChanged(); + } + }; + + private final AdapterView.OnItemSelectedListener mSpinnerListener = + new AdapterView.OnItemSelectedListener() { + + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + showTable(); + boolean empty = (mTable.getAdapter() == null) || + (mTable.getAdapter().getColumnCount() == 0); + if (empty) { + mTable.setAdapter(sEmptyAdapter); + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + mTable.setAdapter(sEmptyAdapter); + } + }; + +} \ No newline at end of file diff --git a/src/com/university/journal/fragments/NewCriteriaDialogFragment.java b/src/com/university/journal/fragments/NewCriteriaDialogFragment.java new file mode 100644 index 0000000..fda3a67 --- /dev/null +++ b/src/com/university/journal/fragments/NewCriteriaDialogFragment.java @@ -0,0 +1,77 @@ +package com.university.journal.fragments; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Locale; +import android.database.Cursor; +import android.support.v4.widget.SimpleCursorAdapter; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.EditText; +import android.widget.Spinner; +import com.university.journal.R; +import com.university.journal.db.DbContract.Subjects; + +public final class NewCriteriaDialogFragment extends NewItemDialogFragment { + + public interface AddListener { + void onConfirm(String name, String date, long subjectId); + }; + + private AddListener mListener; + private EditText mNameEditText, mDateEditText; + private Spinner subjectSpinner; + private SimpleCursorAdapter mSpinnerAdapter; + + public static NewCriteriaDialogFragment newInstance(AddListener listener) { + NewCriteriaDialogFragment frag = new NewCriteriaDialogFragment(); + frag.mListener = listener; + frag.setCancelable(false); + return frag; + } + + @Override + protected View inflate(LayoutInflater inflater) { + View view = inflater.inflate(R.layout.dialog_add_criteria, null, false); + mNameEditText = (EditText) view.findViewById(R.id.name); + mDateEditText = (EditText) view.findViewById(R.id.date); + subjectSpinner = (Spinner) view.findViewById(R.id.subjectSpinner); + + final Cursor cur = getActivity().getContentResolver() + .query(Subjects.CONTENT_URI, Subjects.PROJECTION_ALL, null, null, null); + final String[] from = new String[] { Subjects.NAME }; + final int[] to = new int[] { android.R.id.text1 }; + mSpinnerAdapter = new SimpleCursorAdapter(getActivity(), + android.R.layout.simple_spinner_item, cur, + from, to, SimpleCursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER); + mSpinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + subjectSpinner.setAdapter(mSpinnerAdapter); + return view; + } + + @Override + protected boolean validate() { + if (mNameEditText == null) return false; + final String name = mNameEditText.getText().toString(); + if (TextUtils.isEmpty(name)) return false; + + if (mDateEditText == null) return false; + final String date = mDateEditText.getText().toString(); + if (TextUtils.isEmpty(date)) return false; + try { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.US); + sdf.setLenient(false); + sdf.parse(date); + } catch (ParseException ex) { + return false; + } + + if (subjectSpinner.getSelectedItem() == null) return false; + + if (mListener != null) { + mListener.onConfirm(name, date, subjectSpinner.getSelectedItemId()); + } + return true; + } +} diff --git a/src/com/university/journal/fragments/NewGroupDialogFragment.java b/src/com/university/journal/fragments/NewGroupDialogFragment.java new file mode 100644 index 0000000..18ce8a3 --- /dev/null +++ b/src/com/university/journal/fragments/NewGroupDialogFragment.java @@ -0,0 +1,43 @@ +package com.university.journal.fragments; + +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.EditText; +import com.university.journal.R; + +public final class NewGroupDialogFragment extends NewItemDialogFragment { + + public interface AddListener { + void onConfirm(String name); + }; + + private AddListener mListener; + private EditText mNameEditText; + + public static NewGroupDialogFragment newInstance(AddListener listener) { + NewGroupDialogFragment frag = new NewGroupDialogFragment(); + frag.mListener = listener; + frag.setCancelable(false); + return frag; + } + + @Override + protected View inflate(LayoutInflater inflater) { + View view = inflater.inflate(R.layout.dialog_add_group, null, false); + mNameEditText = (EditText) view.findViewById(R.id.name); + return view; + } + + @Override + protected boolean validate() { + if (mNameEditText == null) return false; + final String name = mNameEditText.getText().toString(); + if (TextUtils.isEmpty(name)) return false; + + if (mListener != null) { + mListener.onConfirm(name); + } + return true; + } +} diff --git a/src/com/university/journal/fragments/NewItemDialogFragment.java b/src/com/university/journal/fragments/NewItemDialogFragment.java new file mode 100644 index 0000000..7f5e676 --- /dev/null +++ b/src/com/university/journal/fragments/NewItemDialogFragment.java @@ -0,0 +1,51 @@ +package com.university.journal.fragments; + +import android.app.AlertDialog; +import android.app.Dialog; +import android.os.Bundle; +import android.support.v4.app.DialogFragment; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.Toast; +import com.university.journal.R; + +public abstract class NewItemDialogFragment extends DialogFragment { + + protected abstract View inflate(LayoutInflater inflater); + + protected abstract boolean validate(); + + @Override + public void onStart() { + super.onStart(); + final AlertDialog dialog = (AlertDialog) getDialog(); + if (dialog != null) { + dialog.getButton(Dialog.BUTTON_POSITIVE) + .setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View v) { + if (validate()) { + dismiss(); + } else { + Toast.makeText(getActivity(), + getString(R.string.validate_error), + Toast.LENGTH_SHORT).show(); + } + } + }); + } + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + View view = inflate(LayoutInflater.from(getActivity())); + + final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + builder.setView(view); + builder.setTitle(R.string.add); + builder.setPositiveButton(android.R.string.ok, null); + builder.setNegativeButton(android.R.string.cancel, null); + return builder.create(); + } +} diff --git a/src/com/university/journal/fragments/NewMarkDialogFragment.java b/src/com/university/journal/fragments/NewMarkDialogFragment.java new file mode 100644 index 0000000..63a45cb --- /dev/null +++ b/src/com/university/journal/fragments/NewMarkDialogFragment.java @@ -0,0 +1,79 @@ +package com.university.journal.fragments; + +import android.os.Bundle; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.EditText; +import com.university.journal.R; +import com.university.journal.entity.Mark; + +public final class NewMarkDialogFragment extends NewItemDialogFragment { + + private static final String ARG_MARK_VALUE = "mark", + ARG_CRITERIA_ID = "criteria_id", + ARG_STUDENT_ID = "student_id", + ARG_TABLE_ROW = "table_row", + ARG_TABLE_COL = "table_col"; + + public interface AddListener { + void onConfirm(int row, int col, Mark mark); + }; + + private AddListener mListener; + private EditText mMarkEditText; + private Mark mMark; + private int mRow, mCol; + + public static NewMarkDialogFragment newInstance(AddListener listener, + int criteriaId, int studentId, String markValue, + int row, int col) { + NewMarkDialogFragment frag = new NewMarkDialogFragment(); + frag.mListener = listener; + frag.setCancelable(false); + + final Bundle args = new Bundle(); + args.putString(ARG_MARK_VALUE, markValue); + args.putInt(ARG_CRITERIA_ID, criteriaId); + args.putInt(ARG_STUDENT_ID, studentId); + args.putInt(ARG_TABLE_ROW, row); + args.putInt(ARG_TABLE_COL, col); + frag.setArguments(args); + return frag; + } + + @Override + protected View inflate(LayoutInflater inflater) { + View view = inflater.inflate(R.layout.dialog_add_mark, null, false); + mMarkEditText = (EditText) view.findViewById(R.id.mark); + + mMark = new Mark(); + final Bundle args = getArguments(); + if (args != null) { + final String markValue = args.getString(ARG_MARK_VALUE); + mMark.setMark(markValue); + mMark.setCriteriaId(args.getInt(ARG_CRITERIA_ID)); + mMark.setStudentId(args.getInt(ARG_STUDENT_ID)); + mRow = args.getInt(ARG_TABLE_ROW); + mCol = args.getInt(ARG_TABLE_COL); + mMarkEditText.setText(markValue); + } + return view; + } + + @Override + protected boolean validate() { + if (mMarkEditText == null) return false; + final String markValue = mMarkEditText.getText().toString(); + if (TextUtils.isEmpty(markValue)) return false; + + // No edit mark - not update. + if (mMark.getMark().equals(markValue)) return true; + mMark.setMark(markValue); + + if (mListener != null) { + mListener.onConfirm(mRow, mCol, mMark); + } + return true; + } +} diff --git a/src/com/university/journal/fragments/NewStudentDialogFragment.java b/src/com/university/journal/fragments/NewStudentDialogFragment.java new file mode 100644 index 0000000..683ebab --- /dev/null +++ b/src/com/university/journal/fragments/NewStudentDialogFragment.java @@ -0,0 +1,72 @@ +package com.university.journal.fragments; + +import android.database.Cursor; +import android.support.v4.widget.SimpleCursorAdapter; +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.EditText; +import android.widget.Spinner; +import com.university.journal.R; +import com.university.journal.db.DbContract.Groups; + +public final class NewStudentDialogFragment extends NewItemDialogFragment { + + public interface AddListener { + void onConfirm(String firstName, String lastName, String middleName, long groupId); + }; + + private AddListener mListener; + private EditText mFirtsNameEditText, mLastNameEditText, mMiddleNameEditText; + private Spinner mGroupSpinner; + private SimpleCursorAdapter mSpinnerAdapter; + + public static NewStudentDialogFragment newInstance(AddListener listener) { + NewStudentDialogFragment frag = new NewStudentDialogFragment(); + frag.mListener = listener; + frag.setCancelable(false); + return frag; + } + + @Override + protected View inflate(LayoutInflater inflater) { + View view = inflater.inflate(R.layout.dialog_add_student, null, false); + mFirtsNameEditText = (EditText) view.findViewById(R.id.firstname); + mLastNameEditText = (EditText) view.findViewById(R.id.lastname); + mMiddleNameEditText = (EditText) view.findViewById(R.id.middlename); + mGroupSpinner = (Spinner) view.findViewById(R.id.groupSpinner); + + final Cursor cur = getActivity().getContentResolver() + .query(Groups.CONTENT_URI, Groups.PROJECTION_ALL, null, null, null); + final String[] from = new String[] { Groups.NAME }; + final int[] to = new int[] { android.R.id.text1 }; + mSpinnerAdapter = new SimpleCursorAdapter(getActivity(), + android.R.layout.simple_spinner_item, cur, + from, to, SimpleCursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER); + mSpinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + mGroupSpinner.setAdapter(mSpinnerAdapter); + return view; + } + + @Override + protected boolean validate() { + if (mFirtsNameEditText == null) return false; + final String firstName = mFirtsNameEditText.getText().toString(); + if (TextUtils.isEmpty(firstName)) return false; + + if (mLastNameEditText == null) return false; + final String lastName = mLastNameEditText.getText().toString(); + if (TextUtils.isEmpty(lastName)) return false; + + if (mMiddleNameEditText == null) return false; + final String middleName = mMiddleNameEditText.getText().toString(); + if (TextUtils.isEmpty(middleName)) return false; + + if (mGroupSpinner.getSelectedItem() == null) return false; + + if (mListener != null) { + mListener.onConfirm(firstName, lastName, middleName, mGroupSpinner.getSelectedItemId()); + } + return true; + } +} diff --git a/src/com/university/journal/fragments/NewSubjectDialogFragment.java b/src/com/university/journal/fragments/NewSubjectDialogFragment.java new file mode 100644 index 0000000..4437585 --- /dev/null +++ b/src/com/university/journal/fragments/NewSubjectDialogFragment.java @@ -0,0 +1,48 @@ +package com.university.journal.fragments; + +import android.text.TextUtils; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.EditText; +import com.university.journal.R; + +public final class NewSubjectDialogFragment extends NewItemDialogFragment { + + public interface AddListener { + void onConfirm(String name, String shortName); + }; + + private AddListener mListener; + private EditText mNameEditText, mShortNameEditText; + + public static NewSubjectDialogFragment newInstance(AddListener listener) { + NewSubjectDialogFragment frag = new NewSubjectDialogFragment(); + frag.mListener = listener; + frag.setCancelable(false); + return frag; + } + + @Override + protected View inflate(LayoutInflater inflater) { + View view = inflater.inflate(R.layout.dialog_add_subject, null, false); + mNameEditText = (EditText) view.findViewById(R.id.name); + mShortNameEditText = (EditText) view.findViewById(R.id.shortName); + return view; + } + + @Override + protected boolean validate() { + if (mNameEditText == null) return false; + final String name = mNameEditText.getText().toString(); + if (TextUtils.isEmpty(name)) return false; + + if (mShortNameEditText == null) return false; + final String shortName = mShortNameEditText.getText().toString(); + if (TextUtils.isEmpty(shortName)) return false; + + if (mListener != null) { + mListener.onConfirm(name, shortName); + } + return true; + } +} diff --git a/src/com/university/journal/fragments/StudentFragment.java b/src/com/university/journal/fragments/StudentFragment.java new file mode 100644 index 0000000..691728b --- /dev/null +++ b/src/com/university/journal/fragments/StudentFragment.java @@ -0,0 +1,105 @@ +package com.university.journal.fragments; + +import android.content.ContentUris; +import android.database.Cursor; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.DialogFragment; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import android.support.v4.widget.SimpleCursorAdapter; +import android.view.View; +import android.widget.AdapterView; +import android.widget.TextView; +import android.widget.Toast; +import com.university.journal.db.DbContract; +import com.university.journal.db.DbContract.Groups; +import com.university.journal.db.DbContract.Students; +import com.university.journal.entity.Student; + +public final class StudentFragment extends DbTableFragment { + + @Override + protected int loaderNumber() { + return 2; + } + + @Override + protected String[] getUIBindFrom() { + return new String[] { Students.LAST_NAME, Students.FIRST_NAME, Students.MIDDLE_NAME }; + } + + @Override + protected int[] getUIBindTo() { + return new int[] { android.R.id.text1 }; + } + + @Override + protected int getUIBindLayoutResource() { + return android.R.layout.simple_list_item_1; + } + + @Override + protected void postInitAdapter(SimpleCursorAdapter adapter) { + adapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() { + + @Override + public boolean setViewValue(View view, Cursor cursor, int columnIndex) { + final String lastName = cursor.getString(1); + final String firstName = cursor.getString(2); + final String middleName = cursor.getString(3); + TextView textView = (TextView) view; + textView.setText(String.format("%s %s %s", + lastName, firstName, middleName)); + return true; + } + }); + getListView().setOnItemClickListener(mItemClickListener); + } + + @Override + protected DialogFragment getAddDataDialogFragment() { + return NewStudentDialogFragment.newInstance(mListener); + } + + @Override + protected void onItemDelete(long id) { + getActivity().getContentResolver() + .delete(ContentUris.withAppendedId(Students.CONTENT_URI, id), null, null); + } + + @Override + public Loader onCreateLoader(int id, Bundle bundle) { + return new CursorLoader(getActivity(), + Students.CONTENT_URI, Students.PROJECTION_ALL, + null, null, Students.LAST_NAME); + } + + private final AdapterView.OnItemClickListener mItemClickListener = + new AdapterView.OnItemClickListener() { + + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { + String selection = String.format("%s.%s = ?", Students.TABLE_NAME, Students._ID); + String[] args = { String.valueOf(id) }; + Cursor cursor = getActivity().getContentResolver() + .query(Uri.withAppendedPath(DbContract.CONTENT_URI, "group_by_student"), + null, selection, args, null); + if ( (cursor != null) && (cursor.moveToFirst()) ) { + String group = cursor.getString(cursor.getColumnIndex(Groups.NAME)); + Toast.makeText(getActivity(), group, Toast.LENGTH_SHORT).show(); + } + } + }; + + private NewStudentDialogFragment.AddListener mListener = new NewStudentDialogFragment.AddListener() { + + @Override + public void onConfirm(String firstName, String lastName, String middleName, long groupId) { + Student obj = new Student(firstName, lastName, middleName, (int) groupId); + getActivity().getContentResolver() + .insert(Students.CONTENT_URI, obj.getContentValues()); + getActivity().getSupportLoaderManager().getLoader(loaderNumber()).forceLoad(); + } + }; +} diff --git a/src/com/university/journal/fragments/SubjectFragment.java b/src/com/university/journal/fragments/SubjectFragment.java new file mode 100644 index 0000000..7636b60 --- /dev/null +++ b/src/com/university/journal/fragments/SubjectFragment.java @@ -0,0 +1,51 @@ +package com.university.journal.fragments; + +import android.content.ContentUris; +import android.database.Cursor; +import android.os.Bundle; +import android.support.v4.app.DialogFragment; +import android.support.v4.content.CursorLoader; +import android.support.v4.content.Loader; +import com.university.journal.db.DbContract.Subjects; +import com.university.journal.entity.Subject; + +public final class SubjectFragment extends DbTableFragment { + + @Override + protected int loaderNumber() { + return 0; + } + + @Override + protected String[] getUIBindFrom() { + return new String[] { Subjects.NAME }; + } + + @Override + protected DialogFragment getAddDataDialogFragment() { + return NewSubjectDialogFragment.newInstance(mListener); + } + + @Override + protected void onItemDelete(long id) { + getActivity().getContentResolver() + .delete(ContentUris.withAppendedId(Subjects.CONTENT_URI, id), null, null); + } + + @Override + public Loader onCreateLoader(int id, Bundle bundle) { + return new CursorLoader(getActivity(), + Subjects.CONTENT_URI, Subjects.PROJECTION_ALL, + null, null, null); + } + + private NewSubjectDialogFragment.AddListener mListener = new NewSubjectDialogFragment.AddListener() { + + @Override + public void onConfirm(String name, String shortName) { + Subject obj = new Subject(name, shortName); + getActivity().getContentResolver().insert(Subjects.CONTENT_URI, obj.getContentValues()); + getActivity().getSupportLoaderManager().getLoader(loaderNumber()).forceLoad(); + } + }; +}