diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 3aea5ad..d53da66 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -23,9 +23,31 @@ android:screenOrientation="portrait" > - - - + + + + + + + + diff --git a/android/res/drawable/drawer_bashgid_logo.png b/android/res/drawable/drawer_bashgid_logo.png index b55ee7e..a6dbf1c 100644 Binary files a/android/res/drawable/drawer_bashgid_logo.png and b/android/res/drawable/drawer_bashgid_logo.png differ diff --git a/android/res/drawable/logo.png b/android/res/drawable/logo.png index b227fa0..c2bee03 100644 Binary files a/android/res/drawable/logo.png and b/android/res/drawable/logo.png differ diff --git a/android/res/layout/fragment_welcome.xml b/android/res/layout/fragment_welcome.xml index a437ebc..3799ab0 100644 --- a/android/res/layout/fragment_welcome.xml +++ b/android/res/layout/fragment_welcome.xml @@ -2,7 +2,6 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@drawable/background" android:orientation="vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" diff --git a/android/res/layout/main_page_list_element.xml b/android/res/layout/main_page_list_element.xml index 7819a9d..736abd8 100644 --- a/android/res/layout/main_page_list_element.xml +++ b/android/res/layout/main_page_list_element.xml @@ -32,7 +32,6 @@ android:background="@drawable/see_more_button" android:gravity="right|center_vertical" android:paddingRight="28dp" - android:text="Узнать больше" android:textSize="16sp" /> diff --git a/android/res/layout/main_page_list_header.xml b/android/res/layout/main_page_list_header.xml index 7369622..0387100 100644 --- a/android/res/layout/main_page_list_header.xml +++ b/android/res/layout/main_page_list_header.xml @@ -9,7 +9,7 @@ tools:context="fishrungames.bashgid.MainPageFragment" > + android:layout_height="wrap_content" /> diff --git a/android/res/menu/global.xml b/android/res/menu/global.xml index 42a675e..307306f 100644 --- a/android/res/menu/global.xml +++ b/android/res/menu/global.xml @@ -1,7 +1,7 @@ - Настройки + Выберите язык: + + Поиск Запись недоступна diff --git a/android/res/values-zh-rCN/strings.xml b/android/res/values-zh-rCN/strings.xml index 76a696d..d210653 100644 --- a/android/res/values-zh-rCN/strings.xml +++ b/android/res/values-zh-rCN/strings.xml @@ -6,6 +6,9 @@ 关闭导航窗口 设置 + 选择语言: + + 搜索标题 无法查看新闻记录 diff --git a/android/res/values/strings.xml b/android/res/values/strings.xml index d5f5ed9..9f1bc9d 100644 --- a/android/res/values/strings.xml +++ b/android/res/values/strings.xml @@ -7,14 +7,17 @@ Settings + Select language: English Русский 中文 - + Search + Search hint + Record is not available Other news… diff --git a/android/src/fishrungames/bashgid/MainActivity.java b/android/src/fishrungames/bashgid/MainActivity.java index 7fc723b..378f8e0 100644 --- a/android/src/fishrungames/bashgid/MainActivity.java +++ b/android/src/fishrungames/bashgid/MainActivity.java @@ -6,103 +6,105 @@ import java.util.Locale; import fishrungames.bashgid.core.NewsManager.NewsRecord; import fishrungames.bashgid.core.db.NewsDataSource; import android.support.v7.app.ActionBarActivity; +import android.support.v7.widget.SearchView; import android.support.v4.app.Fragment; +import android.app.SearchManager; +import android.content.Context; import android.content.res.Configuration; import android.os.Bundle; import android.view.Menu; +import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.support.v4.widget.DrawerLayout; +public class MainActivity extends ActionBarActivity implements NavigationDrawerFragment.NavigationDrawerCallbacks +{ - - -public class MainActivity extends ActionBarActivity -implements NavigationDrawerFragment.NavigationDrawerCallbacks { - - public static final String TAG_FROM_MAINFRAGMENT_TO_NEWSRECORDFRAGMENT = "TAG_FROM_MAINFRAGMENT_TO_NEWSRECORDFRAGMENT"; public static final String TAG_FROM_NEWSLISTFRAGMENT_TO_NEWSRECORDFRAGMENT = "TAG_FROM_NEWSLISTFRAGMENT_TO_NEWSRECORDFRAGMENT"; - - static MainActivity instance = null; - - private NavigationDrawerFragment mNavigationDrawerFragment = null; - - public NewsDataSource newsDataSource; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setContentView(R.layout.activity_main); - - SetupDrawer(); - - getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, new WelcomeFragment()).commit(); - - instance = this; - - newsDataSource = new NewsDataSource(this); - - newsDataSource.open(); - } - - public static MainActivity getInstance() - { - return instance; - - } - - - public void OnSelectEnglish(View view) + static MainActivity instance = null; + + private NavigationDrawerFragment mNavigationDrawerFragment = null; + + public NewsDataSource newsDataSource = null; + + @Override + protected void onCreate(Bundle savedInstanceState) { - Locale.setDefault(Locale.US); - Configuration config = new Configuration(); - config.locale = Locale.US; - getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics()); - - getSupportActionBar().setTitle(R.string.app_name); - - SetupDrawer(); - + super.onCreate(savedInstanceState); + + setContentView(R.layout.activity_main); + + SetupDrawer(); + + getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, new WelcomeFragment()).commit(); + + instance = this; + + + newsDataSource = new NewsDataSource(this); + + newsDataSource.open(); + + } + + public static MainActivity getInstance() + { + return instance; + + } + + public void OnSelectEnglish(View view) + { + Locale.setDefault(Locale.US); + Configuration config = new Configuration(); + config.locale = Locale.US; + getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics()); + + getSupportActionBar().setTitle(R.string.app_name); + + SetupDrawer(); + OpenMainScreen(); } - + public void OnSelectRussian(View view) { - Locale locale = new Locale("ru"); - Locale.setDefault(locale); - Configuration config = new Configuration(); - config.locale = locale; - getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics()); - - getSupportActionBar().setTitle(R.string.app_name); - SetupDrawer(); - + Locale locale = new Locale("ru"); + Locale.setDefault(locale); + Configuration config = new Configuration(); + config.locale = locale; + getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics()); + + getSupportActionBar().setTitle(R.string.app_name); + SetupDrawer(); + OpenMainScreen(); } - + public void OnSelectChinese(View view) { - Locale.setDefault(Locale.CHINA); - Configuration config = new Configuration(); - config.locale = Locale.CHINA; - getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics()); - - getSupportActionBar().setTitle(R.string.app_name); - SetupDrawer(); - + Locale.setDefault(Locale.CHINA); + Configuration config = new Configuration(); + config.locale = Locale.CHINA; + getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics()); + + getSupportActionBar().setTitle(R.string.app_name); + SetupDrawer(); + OpenMainScreen(); } - + public void OnSelectSemiDetails(Integer pos) { - //getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, new SemiDetailsFragment()).addToBackStack(null).commit(); + // getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, + // new SemiDetailsFragment()).addToBackStack(null).commit(); } - + public void OpenMainScreen() { @@ -110,7 +112,7 @@ implements NavigationDrawerFragment.NavigationDrawerCallbacks { mNavigationDrawerFragment.EnableDrawer(); } - + public void OpenNewsScreen() { @@ -119,122 +121,145 @@ implements NavigationDrawerFragment.NavigationDrawerCallbacks { mNavigationDrawerFragment.EnableDrawer(); } + public void OpenSettingsScreen() + { + + getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new SettingsFragment()).commit(); + + mNavigationDrawerFragment.EnableDrawer(); + } + public void OpenNewsRecordScreen(NewsRecord newsRecord, String tag) { - //Xperimental -- addToBackStack provoke error "Class not found". Need to resolve somehow! - //getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new NewsRecordFragment(newsRecord)).addToBackStack(null).commit(); - - //I added workaround: - + // Xperimental -- addToBackStack provoke error "Class not found". Need + // to resolve somehow! + // getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, + // new NewsRecordFragment(newsRecord)).addToBackStack(null).commit(); + + // I added workaround: + NewsRecordFragment newsRecordFragment = new NewsRecordFragment(newsRecord); - + getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, newsRecordFragment, tag).commit(); mNavigationDrawerFragment.EnableDrawer(); } - + @Override public void onAttachFragment(Fragment fragment) { super.onAttachFragment(fragment); - + } - + public void SetupDrawer() { - - - mNavigationDrawerFragment = (NavigationDrawerFragment) - getSupportFragmentManager().findFragmentById(R.id.navigation_drawer); + + mNavigationDrawerFragment = (NavigationDrawerFragment) getSupportFragmentManager().findFragmentById(R.id.navigation_drawer); if (mNavigationDrawerFragment != null) { - - // Set up the drawer. - mNavigationDrawerFragment.setUp( - R.id.navigation_drawer, - (DrawerLayout) findViewById(R.id.drawer_layout)); + + // Set up the drawer. + mNavigationDrawerFragment.setUp(R.id.navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout)); } } + @Override + public boolean onCreateOptionsMenu(Menu menu) + { + + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.global, menu); + + /* + SearchManager searchManager = + (SearchManager) getSystemService(Context.SEARCH_SERVICE); + SearchView searchView = + (SearchView) menu.findItem(R.id.action_search).getActionView(); + searchView.setSearchableInfo( + searchManager.getSearchableInfo(getComponentName())); +*/ + + + return super.onCreateOptionsMenu(menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) + { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + if (id == R.id.action_settings) + { + return true; + } + return super.onOptionsItemSelected(item); + } + + @Override + public void onNavigationDrawerItemSelected(int position) + { + if (position == 1) + { + OpenMainScreen(); + } + else if (position == 2) + { + OpenNewsScreen(); + } + else if (position == 8) + { + OpenSettingsScreen(); + } + } + + @Override + public void onBackPressed() + { + + if (getSupportFragmentManager().findFragmentByTag(TAG_FROM_MAINFRAGMENT_TO_NEWSRECORDFRAGMENT) != null) + { + OpenMainScreen(); + + return; + } + + if (getSupportFragmentManager().findFragmentByTag(TAG_FROM_NEWSLISTFRAGMENT_TO_NEWSRECORDFRAGMENT) != null) + { + OpenNewsScreen(); + + return; + } + + super.onBackPressed(); + + /* + * AddToBackStack is falling. Here is workaround + * if(getSupportFragmentManager().getBackStackEntryCount() != 0) { + * getSupportFragmentManager().popBackStack(); } else { + * super.onBackPressed(); } + */ + } + + @Override + protected void onResume() + { + newsDataSource.open(); + super.onResume(); + } + + @Override + protected void onPause() + { + newsDataSource.close(); + + super.onPause(); + } + - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - - return super.onCreateOptionsMenu(menu); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - // Handle action bar item clicks here. The action bar will - // automatically handle clicks on the Home/Up button, so long - // as you specify a parent activity in AndroidManifest.xml. - int id = item.getItemId(); - if (id == R.id.action_settings) { - return true; - } - return super.onOptionsItemSelected(item); - } - - - @Override - public void onNavigationDrawerItemSelected(int position) - { - if (position == 1) - { - OpenMainScreen(); - } - else if (position == 2) - { - OpenNewsScreen(); - } - } - - - - @Override - public void onBackPressed() { - - if ( getSupportFragmentManager().findFragmentByTag(TAG_FROM_MAINFRAGMENT_TO_NEWSRECORDFRAGMENT) != null) - { - OpenMainScreen(); - - return; - } - - if ( getSupportFragmentManager().findFragmentByTag(TAG_FROM_NEWSLISTFRAGMENT_TO_NEWSRECORDFRAGMENT) != null) - { - OpenNewsScreen(); - - return; - } - - super.onBackPressed(); - - - /* - * AddToBackStack is falling. Here is workaround - if(getSupportFragmentManager().getBackStackEntryCount() != 0) { - getSupportFragmentManager().popBackStack(); - } else { - super.onBackPressed(); - }*/ - } - - @Override - protected void onResume() { - //Xperimental -- opening and closing leads to fail - //newsDataSource.open(); - super.onResume(); - } - - @Override - protected void onPause() { - //Xperimental -- opening and closing leads to fail - //newsDataSource.close(); - super.onPause(); - } } diff --git a/android/src/fishrungames/bashgid/MainPageFragment.java b/android/src/fishrungames/bashgid/MainPageFragment.java index 8650de6..bad6c35 100644 --- a/android/src/fishrungames/bashgid/MainPageFragment.java +++ b/android/src/fishrungames/bashgid/MainPageFragment.java @@ -47,7 +47,7 @@ public class MainPageFragment extends Fragment implements NewsUpdatedCallbackInt ListView listView; View header; - NewsManager.RemoveCallbackInterface downloadNewsCanceller; + NewsManager.RemoveCallbackInterface downloadNewsCanceller = null; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -84,7 +84,11 @@ public class MainPageFragment extends Fragment implements NewsUpdatedCallbackInt @Override public void onDestroyView() { - downloadNewsCanceller.RemoveCallback(); + if (downloadNewsCanceller != null) + { + downloadNewsCanceller.RemoveCallback(); + downloadNewsCanceller = null; + } super.onDestroyView(); } diff --git a/android/src/fishrungames/bashgid/NavigationDrawerFragment.java b/android/src/fishrungames/bashgid/NavigationDrawerFragment.java index 3faf8f8..ca38ef3 100644 --- a/android/src/fishrungames/bashgid/NavigationDrawerFragment.java +++ b/android/src/fishrungames/bashgid/NavigationDrawerFragment.java @@ -3,9 +3,11 @@ package fishrungames.bashgid; import android.support.v7.app.ActionBarActivity; import android.app.Activity; +import android.app.SearchManager; import android.support.v7.app.ActionBar; import android.support.v4.app.Fragment; import android.support.v7.app.ActionBarDrawerToggle; +import android.support.v7.widget.SearchView; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; import android.content.Context; @@ -258,10 +260,11 @@ public class NavigationDrawerFragment extends Fragment { // showGlobalContextActionBar, which controls the top-left area of the action bar. - inflater.inflate(R.menu.global, menu); + //inflater.inflate(R.menu.global, menu); showGlobalContextActionBar(); super.onCreateOptionsMenu(menu, inflater); + } @Override @@ -271,6 +274,11 @@ public class NavigationDrawerFragment extends Fragment { if (mDrawerToggle.onOptionsItemSelected(item)) { return true; } + + if (item.getItemId() == R.id.action_search) { + + return true; + } if (item.getItemId() == R.id.action_settings) { Toast.makeText(getActivity(), "Example action.", Toast.LENGTH_SHORT).show(); diff --git a/android/src/fishrungames/bashgid/NewsListFragment.java b/android/src/fishrungames/bashgid/NewsListFragment.java index a258b8b..d6d6e26 100644 --- a/android/src/fishrungames/bashgid/NewsListFragment.java +++ b/android/src/fishrungames/bashgid/NewsListFragment.java @@ -31,7 +31,7 @@ public class NewsListFragment extends Fragment implements NewsUpdatedCallbackInt ArrayList newsRecordArr; - NewsManager.RemoveCallbackInterface downloadNewsCanceller; + NewsManager.RemoveCallbackInterface downloadNewsCanceller = null; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -57,7 +57,11 @@ public class NewsListFragment extends Fragment implements NewsUpdatedCallbackInt @Override public void onDestroyView() { - downloadNewsCanceller.RemoveCallback(); + if (downloadNewsCanceller != null) + { + downloadNewsCanceller.RemoveCallback(); + downloadNewsCanceller = null; + } super.onDestroyView(); } diff --git a/android/src/fishrungames/bashgid/SettingsFragment.java b/android/src/fishrungames/bashgid/SettingsFragment.java new file mode 100644 index 0000000..41310c0 --- /dev/null +++ b/android/src/fishrungames/bashgid/SettingsFragment.java @@ -0,0 +1,22 @@ +package fishrungames.bashgid; + + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + + +public class SettingsFragment extends Fragment +{ + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + + View rootView = inflater.inflate(R.layout.fragment_settings_page, container, false); + + + return rootView; + } +} diff --git a/android/src/fishrungames/bashgid/core/ImageManager.java b/android/src/fishrungames/bashgid/core/ImageManager.java index 29075ed..a00c619 100644 --- a/android/src/fishrungames/bashgid/core/ImageManager.java +++ b/android/src/fishrungames/bashgid/core/ImageManager.java @@ -5,6 +5,8 @@ import java.io.FileOutputStream; import java.io.InputStream; import java.net.URL; import java.util.HashMap; +import java.util.HashSet; +import java.util.concurrent.Semaphore; import fishrungames.bashgid.MainActivity; import fishrungames.bashgid.R; @@ -20,6 +22,10 @@ public class ImageManager { static ImageManager instance = null; + private volatile Semaphore downloadingFileNameSetMutex = new Semaphore(1, true); + private volatile HashSet downloadingFileNameSet = new HashSet(); + + public static ImageManager getInstance() { if (instance == null) @@ -43,6 +49,8 @@ public class ImageManager { public void ApplyImageToImageView(ImageView imageView, String imageId) { + //Xperimental -- Should be called only in main thread + if (imageId.startsWith("R.drawable.")) { Integer resourceId = PredefinedImageId.get(imageId); @@ -64,14 +72,40 @@ public class ImageManager { if (f.exists()) { - Bitmap bitmap = BitmapFactory.decodeFile( MainActivity.getInstance().getFileStreamPath(fileName).getAbsolutePath()); - imageView.setImageBitmap(bitmap); + boolean fileIsBusy = false; + + + try + { + downloadingFileNameSetMutex.acquire(); + try + { + fileIsBusy = downloadingFileNameSet.contains(fileName); + } finally + { + downloadingFileNameSetMutex.release(); + } + } catch (InterruptedException ie) + { + Log.e("Error in ApplyImageToImageView", "Error in ApplyImageToImageView"); + } + + + if (!fileIsBusy) + { + + Bitmap bitmap = BitmapFactory.decodeFile( MainActivity.getInstance().getFileStreamPath(fileName).getAbsolutePath()); + + imageView.setImageBitmap(bitmap); + + } + + + } else { - Log.e("Error: ", "Image requested but not downloaded: " + imageId); - imageView.setImageResource(R.drawable.transparent); } } @@ -97,26 +131,76 @@ public class ImageManager { return; } - try { - InputStream is = (InputStream) new URL(imageUrl).getContent(); - - FileOutputStream out = null; - - out = MainActivity.getInstance().openFileOutput(fileName, Context.MODE_PRIVATE); - - byte [] buf = new byte[1024]; - - int numRead; - - while ( (numRead = is.read(buf) ) >= 0) { - out.write(buf, 0, numRead); - } - - out.close(); + boolean fileIsBusy = false; + + + try + { + downloadingFileNameSetMutex.acquire(); + try + { + if (downloadingFileNameSet.contains(fileIsBusy)) + { + fileIsBusy = true; + } + else + { + downloadingFileNameSet.add(fileName); + } + } finally + { + downloadingFileNameSetMutex.release(); + } + } catch (InterruptedException ie) + { + Log.e("Error in DownloadImageIfNeeded", "Error in DownloadImageIfNeeded"); + } + + if (!fileIsBusy) + { + + try { + InputStream is = (InputStream) new URL(imageUrl).getContent(); + + FileOutputStream out = null; + + out = MainActivity.getInstance().openFileOutput(fileName, Context.MODE_PRIVATE); + + byte [] buf = new byte[1024]; + + int numRead; + + while ( (numRead = is.read(buf) ) >= 0) { + out.write(buf, 0, numRead); + } + + out.close(); + + + } catch (Exception e) { + e.printStackTrace(); + } + + + try + { + downloadingFileNameSetMutex.acquire(); + try + { - } catch (Exception e) { - e.printStackTrace(); - } + downloadingFileNameSet.remove(fileName); + + } finally + { + downloadingFileNameSetMutex.release(); + } + } catch (InterruptedException ie) + { + Log.e("Error in DownloadImageIfNeeded", "Error in DownloadImageIfNeeded"); + } + + + } } public String GetImageLocalUrl(String imageUrl) diff --git a/android/src/fishrungames/bashgid/core/NewsManager.java b/android/src/fishrungames/bashgid/core/NewsManager.java index 10c1804..74d0b20 100644 --- a/android/src/fishrungames/bashgid/core/NewsManager.java +++ b/android/src/fishrungames/bashgid/core/NewsManager.java @@ -4,16 +4,38 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; +import java.util.concurrent.Semaphore; + +import android.util.Log; import fishrungames.bashgid.MainActivity; +import fishrungames.bashgid.core.db.BashgidSqliteHelper; public class NewsManager { - private static NewsManager instance = null; + private static final String [] urlArr = { + "https://www.bashkortostan.ru/presscenter/news/rss/", + "http://www.bashedu.ru/rss.xml", + "http://www.bashinform.ru/rss/all.xml", + "http://www.minkult-rb.ru/news/rss/", + "http://02.mvd.ru/news/rss/" + }; + + private static NewsManager instance = null; + + private static volatile boolean SynchronizationInProcess = false; //Not strict, so don't need mutex + + private volatile Semaphore newsRecordMutex = new Semaphore(1, true); + private ArrayList newsRecord = new ArrayList(); + + + + public static boolean CanCallSynchronization() + { + return SynchronizationInProcess; + } - //Xperimental -- need do something with this. Change to semaphore? - public static volatile boolean SynchronizationInProcess = false; public static NewsManager GetInstance() @@ -60,16 +82,18 @@ public class NewsManager { } } - ArrayList newsRecord = new ArrayList(); - + public RemoveCallbackInterface DownloadNews(NewsUpdatedCallbackInterface callback) { - - DownloadNewsRunnable downloadNewsRunnable = new DownloadNewsRunnable(callback); - - Thread thread = new Thread(downloadNewsRunnable); - - thread.start(); + DownloadNewsRunnable downloadNewsRunnable = null; + if (CanCallSynchronization()) + { + downloadNewsRunnable = new DownloadNewsRunnable(callback); + + Thread thread = new Thread(downloadNewsRunnable); + + thread.start(); + } return downloadNewsRunnable; } @@ -77,70 +101,123 @@ public class NewsManager { class DownloadNewsRunnable implements Runnable, RemoveCallbackInterface { - NewsUpdatedCallbackInterface callback; + private volatile Semaphore callbackMutex = new Semaphore(1, true); + NewsUpdatedCallbackInterface callback = null; public DownloadNewsRunnable(NewsUpdatedCallbackInterface callback) { - this.callback = callback; + try + { + callbackMutex.acquire(); + try + { + + this.callback = callback; + + } finally + { + callbackMutex.release(); + } + } catch (InterruptedException ie) + { + Log.e("Error in DownloadNewsRunnable", "Error in DownloadNewsRunnable"); + } + } @Override public void RemoveCallback() { - callback = null; + + try + { + callbackMutex.acquire(); + try + { + + callback = null; + + } finally + { + callbackMutex.release(); + } + } catch (InterruptedException ie) + { + Log.e("Error in DownloadNewsRunnable", "Error in DownloadNewsRunnable"); + } } @Override public void run() { - if (SynchronizationInProcess) - { - return; - } - - SynchronizationInProcess = true; - - final String [] urlArr = { - "https://www.bashkortostan.ru/presscenter/news/rss/", - "http://www.bashedu.ru/rss.xml", - "http://www.bashinform.ru/rss/all.xml", - "http://www.minkult-rb.ru/news/rss/", - "http://02.mvd.ru/news/rss/" - - }; - + + + SynchronizationInProcess = true; + + ArrayList localNewsRecord = MainActivity.getInstance().newsDataSource.getNews(); ArrayList imageToDownloadList = new ArrayList(); for (int i = 0; i < urlArr.length; i++) { - + LoadNewsAndImagesFromRss2(urlArr[i], localNewsRecord, imageToDownloadList); - Collections.sort(localNewsRecord, new NewsSortComparator()); - - //NewsSortComparator - - //Xperimental -- need mutex right here - newsRecord = localNewsRecord; - - MainActivity.getInstance().newsDataSource.replaceNews(newsRecord); - - //Xperimental -- need mutex here, too - if (callback != null) - { - callback.OnNewsUpdated(); - } - } + Collections.sort(localNewsRecord, new NewsSortComparator()); + + + try + { + newsRecordMutex.acquire(); + try + { + + newsRecord = localNewsRecord; + + MainActivity.getInstance().newsDataSource.replaceNews(newsRecord); + + } finally + { + newsRecordMutex.release(); + } + } catch (InterruptedException ie) + { + Log.e("Error in DownloadNewsRunnable", "Error in DownloadNewsRunnable"); + } + + + try + { + callbackMutex.acquire(); + try + { + + if (callback != null) + { + callback.OnNewsUpdated(); + } + + } finally + { + callbackMutex.release(); + } + } catch (InterruptedException ie) + { + Log.e("Error in DownloadNewsRunnable", "Error in DownloadNewsRunnable"); + } + + for (String imageToDownload : imageToDownloadList) { ImageManager.getInstance().DownloadImageIfNeeded(imageToDownload); } - SynchronizationInProcess = false; + + SynchronizationInProcess = false; + } } @@ -157,14 +234,46 @@ public class NewsManager { public NewsManager() { - newsRecord = MainActivity.getInstance().newsDataSource.getNews(); + try + { + newsRecordMutex.acquire(); + try + { + newsRecord = MainActivity.getInstance().newsDataSource.getNews(); + + } finally + { + newsRecordMutex.release(); + } + } catch (InterruptedException ie) + { + Log.e("Error in NewsManager constructor", "Error in NewsManager constructor"); + } } public ArrayList getNews() { - return newsRecord; + ArrayList result = new ArrayList(); + + try + { + newsRecordMutex.acquire(); + try + { + result.addAll(newsRecord); + + } finally + { + newsRecordMutex.release(); + } + } catch (InterruptedException ie) + { + Log.e("Error in NewsManager constructor", "Error in NewsManager constructor"); + } + + return result; } diff --git a/android/src/fishrungames/bashgid/core/XmlProcessor.java b/android/src/fishrungames/bashgid/core/XmlProcessor.java index 37a4983..bc1904c 100644 --- a/android/src/fishrungames/bashgid/core/XmlProcessor.java +++ b/android/src/fishrungames/bashgid/core/XmlProcessor.java @@ -7,6 +7,7 @@ import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; +import org.w3c.dom.DOMException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -36,6 +37,11 @@ public class XmlProcessor { Log.e("Error: ", e.getMessage()); return null; + } catch (DOMException e) + { + //Xperimental -- need to learn more about this exception + Log.e("Error: ", e.getMessage()); + return null; } catch (SAXException e) { Log.e("Error: ", e.getMessage()); diff --git a/android/src/fishrungames/bashgid/core/db/NewsDataSource.java b/android/src/fishrungames/bashgid/core/db/NewsDataSource.java index 7c6a997..9be548e 100644 --- a/android/src/fishrungames/bashgid/core/db/NewsDataSource.java +++ b/android/src/fishrungames/bashgid/core/db/NewsDataSource.java @@ -4,6 +4,7 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Locale; +import java.util.concurrent.Semaphore; import fishrungames.bashgid.core.NewsManager; @@ -12,6 +13,7 @@ import android.content.Context; import android.database.Cursor; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; +import android.util.Log; public class NewsDataSource { @@ -19,25 +21,82 @@ public class NewsDataSource //Xperimental -- move somewhere public static final SimpleDateFormat iso8601Format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US); - private SQLiteDatabase database; + private SQLiteDatabase database = null; - private BashgidSqliteHelper dbHelper; + private BashgidSqliteHelper dbHelper = null; private String[] allColumns = { BashgidSqliteHelper.COLUMN_ID, BashgidSqliteHelper.COLUMN_TITLE, BashgidSqliteHelper.COLUMN_DESCRIPTION, BashgidSqliteHelper.COLUMN_IMAGE_ID, BashgidSqliteHelper.COLUMN_PUBDATE }; + + + private final Semaphore mutex = new Semaphore(1, true); - public NewsDataSource(Context context) { - dbHelper = new BashgidSqliteHelper(context); + public NewsDataSource(Context context) + { + + try + { + mutex.acquire(); + try + { + + dbHelper = new BashgidSqliteHelper(context); + + } finally + { + mutex.release(); + } + } catch (InterruptedException ie) + { + Log.e("Error when creating BashgidSqliteHelper", "Error when creating BashgidSqliteHelper"); + } } public void open() throws SQLException { - database = dbHelper.getWritableDatabase(); + try + { + mutex.acquire(); + try + { + + database = dbHelper.getWritableDatabase(); + + } finally + { + mutex.release(); + } + } catch (InterruptedException ie) + { + Log.e("Error when opening NewsDataSource", "Error when opening NewsDataSource"); + + database = null; + } + } public void close() { - dbHelper.close(); + try + { + mutex.acquire(); + try + { + if (dbHelper != null) + { + dbHelper.close(); + database = null; + } + + } finally + { + mutex.release(); + } + } catch (InterruptedException ie) + { + Log.e("Error when closing NewsDataSource", "Error when closing NewsDataSource"); + } + } public void createNewsRecord(NewsManager.NewsRecord newsRecord) @@ -50,14 +109,52 @@ public class NewsDataSource values.put(BashgidSqliteHelper.COLUMN_IMAGE_ID, newsRecord.imageId); values.put(BashgidSqliteHelper.COLUMN_PUBDATE, iso8601Format.format(newsRecord.pubDate)); - database.insert(BashgidSqliteHelper.TABLE_NEWS, null, values); + + try + { + mutex.acquire(); + try + { + + if (database != null) + { + database.insert(BashgidSqliteHelper.TABLE_NEWS, null, values); + } + + } finally + { + mutex.release(); + } + } catch (InterruptedException ie) + { + Log.e("Error when calling createNewsRecord", "Error when calling createNewsRecord"); + } } public void replaceNews(ArrayList newsRecordArr) { - database.delete(BashgidSqliteHelper.TABLE_NEWS, null, null); + try + { + mutex.acquire(); + try + { + + if (database != null) + { + database.delete(BashgidSqliteHelper.TABLE_NEWS, null, null); + } + + } finally + { + mutex.release(); + } + } catch (InterruptedException ie) + { + Log.e("Error when calling replaceNews", "Error when calling replaceNews"); + } + for (NewsManager.NewsRecord newsRecord : newsRecordArr) { @@ -70,18 +167,39 @@ public class NewsDataSource { ArrayList newsRecordArr = new ArrayList(); - - Cursor cursor = database.query(BashgidSqliteHelper.TABLE_NEWS, allColumns, null, null, null, null, null); - - cursor.moveToFirst(); - while (!cursor.isAfterLast()) +/* + try { - NewsManager.NewsRecord newsRecord = cursorToNewsRecord(cursor); - newsRecordArr.add(newsRecord); - cursor.moveToNext(); + mutex.acquire(); + try + { +*/ + if (database != null) + { + Cursor cursor = database.query(BashgidSqliteHelper.TABLE_NEWS, allColumns, null, null, null, null, null); + + cursor.moveToFirst(); + + while (!cursor.isAfterLast()) + { + NewsManager.NewsRecord newsRecord = cursorToNewsRecord(cursor); + newsRecordArr.add(newsRecord); + cursor.moveToNext(); + } + + cursor.close(); + } + + /* + } finally + { + mutex.release(); + } + } catch (InterruptedException ie) + { + Log.e("Error when calling replaceNews", "Error when calling replaceNews"); } - // make sure to close the cursor - cursor.close(); + */ return newsRecordArr; }