Settings + adding mutexes whenever possible.

This commit is contained in:
Vladislav Khorev 2014-11-19 12:08:10 +00:00
parent 6f234a435c
commit 75dd42a703
20 changed files with 678 additions and 271 deletions

View File

@ -25,7 +25,29 @@
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
<meta-data android:name="android.app.searchable"
android:resource="@xml/searchable" />
</activity> </activity>
<!--
<activity
android:name=".SearchResultActivity"
android:label="@string/app_name"
android:configChanges="orientation"
android:screenOrientation="portrait" >
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
</intent-filter>
<meta-data android:name="android.app.searchable"
android:resource="@xml/searchable" />
</activity>
-->
</application> </application>
</manifest> </manifest>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -2,7 +2,6 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="@drawable/background"
android:orientation="vertical" android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingLeft="@dimen/activity_horizontal_margin"

View File

@ -32,7 +32,6 @@
android:background="@drawable/see_more_button" android:background="@drawable/see_more_button"
android:gravity="right|center_vertical" android:gravity="right|center_vertical"
android:paddingRight="28dp" android:paddingRight="28dp"
android:text="Узнать больше"
android:textSize="16sp" /> android:textSize="16sp" />
</LinearLayout> </LinearLayout>

View File

@ -9,7 +9,7 @@
tools:context="fishrungames.bashgid.MainPageFragment" > tools:context="fishrungames.bashgid.MainPageFragment" >
<TextView <TextView
android:id="@+id/textView1" android:id="@+id/selectLanguageTextView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"

View File

@ -32,14 +32,12 @@
android:gravity="left|center_vertical" android:gravity="left|center_vertical"
android:minHeight="0dip" android:minHeight="0dip"
android:minWidth="0dip" android:minWidth="0dip"
android:text="yrrttedf"
android:textColor="@color/text_grey" /> android:textColor="@color/text_grey" />
<TextView <TextView
android:id="@+id/textView" android:id="@+id/textView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content" />
android:text="rtrrtrt" />
</LinearLayout> </LinearLayout>

View File

@ -1,7 +1,7 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" > xmlns:app="http://schemas.android.com/apk/res-auto" >
<item android:id="@+id/search" <item android:id="@+id/action_search"
android:title="@string/search_title" android:title="@string/search_title"
android:icon="@drawable/abc_ic_search_api_mtrl_alpha" android:icon="@drawable/abc_ic_search_api_mtrl_alpha"
app:showAsAction="always" app:showAsAction="always"

View File

@ -8,6 +8,9 @@
<string name="action_settings">Настройки</string> <string name="action_settings">Настройки</string>
<string name="select_language">Выберите язык:</string>
<string name="search_title">Поиск</string> <string name="search_title">Поиск</string>
<string name="news_record_not_available">Запись недоступна</string> <string name="news_record_not_available">Запись недоступна</string>

View File

@ -6,6 +6,9 @@
<string name="navigation_drawer_close">关闭导航窗口</string> <string name="navigation_drawer_close">关闭导航窗口</string>
<string name="action_settings">设置</string> <string name="action_settings">设置</string>
<string name="select_language">选择语言:</string>
<string name="search_title">搜索标题</string> <string name="search_title">搜索标题</string>
<string name="news_record_not_available">无法查看新闻记录</string> <string name="news_record_not_available">无法查看新闻记录</string>

View File

@ -7,6 +7,7 @@
<string name="action_settings">Settings</string> <string name="action_settings">Settings</string>
<string name="select_language">Select language:</string>
<string name="lang_english">English</string> <string name="lang_english">English</string>
<string name="lang_russian">Русский</string> <string name="lang_russian">Русский</string>
@ -15,6 +16,8 @@
<string name="search_title">Search</string> <string name="search_title">Search</string>
<string name="search_hint">Search hint</string>
<string name="news_record_not_available">Record is not available</string> <string name="news_record_not_available">Record is not available</string>
<string name="other_news">Other news…</string> <string name="other_news">Other news…</string>

View File

@ -6,22 +6,22 @@ import java.util.Locale;
import fishrungames.bashgid.core.NewsManager.NewsRecord; import fishrungames.bashgid.core.NewsManager.NewsRecord;
import fishrungames.bashgid.core.db.NewsDataSource; import fishrungames.bashgid.core.db.NewsDataSource;
import android.support.v7.app.ActionBarActivity; import android.support.v7.app.ActionBarActivity;
import android.support.v7.widget.SearchView;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.app.SearchManager;
import android.content.Context;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.os.Bundle; import android.os.Bundle;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.support.v4.widget.DrawerLayout; 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_MAINFRAGMENT_TO_NEWSRECORDFRAGMENT = "TAG_FROM_MAINFRAGMENT_TO_NEWSRECORDFRAGMENT";
public static final String TAG_FROM_NEWSLISTFRAGMENT_TO_NEWSRECORDFRAGMENT = "TAG_FROM_NEWSLISTFRAGMENT_TO_NEWSRECORDFRAGMENT"; public static final String TAG_FROM_NEWSLISTFRAGMENT_TO_NEWSRECORDFRAGMENT = "TAG_FROM_NEWSLISTFRAGMENT_TO_NEWSRECORDFRAGMENT";
@ -30,43 +30,44 @@ implements NavigationDrawerFragment.NavigationDrawerCallbacks {
private NavigationDrawerFragment mNavigationDrawerFragment = null; private NavigationDrawerFragment mNavigationDrawerFragment = null;
public NewsDataSource newsDataSource; public NewsDataSource newsDataSource = null;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState); {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
SetupDrawer(); SetupDrawer();
getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, new WelcomeFragment()).commit(); getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, new WelcomeFragment()).commit();
instance = this; instance = this;
newsDataSource = new NewsDataSource(this);
newsDataSource.open(); newsDataSource = new NewsDataSource(this);
} newsDataSource.open();
public static MainActivity getInstance() }
{
public static MainActivity getInstance()
{
return instance; return instance;
} }
public void OnSelectEnglish(View view)
public void OnSelectEnglish(View view)
{ {
Locale.setDefault(Locale.US); Locale.setDefault(Locale.US);
Configuration config = new Configuration(); Configuration config = new Configuration();
config.locale = Locale.US; config.locale = Locale.US;
getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics()); getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics());
getSupportActionBar().setTitle(R.string.app_name); getSupportActionBar().setTitle(R.string.app_name);
SetupDrawer(); SetupDrawer();
OpenMainScreen(); OpenMainScreen();
} }
@ -74,33 +75,34 @@ implements NavigationDrawerFragment.NavigationDrawerCallbacks {
public void OnSelectRussian(View view) public void OnSelectRussian(View view)
{ {
Locale locale = new Locale("ru"); Locale locale = new Locale("ru");
Locale.setDefault(locale); Locale.setDefault(locale);
Configuration config = new Configuration(); Configuration config = new Configuration();
config.locale = locale; config.locale = locale;
getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics()); getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics());
getSupportActionBar().setTitle(R.string.app_name); getSupportActionBar().setTitle(R.string.app_name);
SetupDrawer(); SetupDrawer();
OpenMainScreen(); OpenMainScreen();
} }
public void OnSelectChinese(View view) public void OnSelectChinese(View view)
{ {
Locale.setDefault(Locale.CHINA); Locale.setDefault(Locale.CHINA);
Configuration config = new Configuration(); Configuration config = new Configuration();
config.locale = Locale.CHINA; config.locale = Locale.CHINA;
getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics()); getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics());
getSupportActionBar().setTitle(R.string.app_name); getSupportActionBar().setTitle(R.string.app_name);
SetupDrawer(); SetupDrawer();
OpenMainScreen(); OpenMainScreen();
} }
public void OnSelectSemiDetails(Integer pos) 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() public void OpenMainScreen()
@ -119,12 +121,22 @@ implements NavigationDrawerFragment.NavigationDrawerCallbacks {
mNavigationDrawerFragment.EnableDrawer(); mNavigationDrawerFragment.EnableDrawer();
} }
public void OpenSettingsScreen()
{
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new SettingsFragment()).commit();
mNavigationDrawerFragment.EnableDrawer();
}
public void OpenNewsRecordScreen(NewsRecord newsRecord, String tag) public void OpenNewsRecordScreen(NewsRecord newsRecord, String tag)
{ {
//Xperimental -- addToBackStack provoke error "Class not found". Need to resolve somehow! // Xperimental -- addToBackStack provoke error "Class not found". Need
//getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new NewsRecordFragment(newsRecord)).addToBackStack(null).commit(); // to resolve somehow!
// getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,
// new NewsRecordFragment(newsRecord)).addToBackStack(null).commit();
//I added workaround: // I added workaround:
NewsRecordFragment newsRecordFragment = new NewsRecordFragment(newsRecord); NewsRecordFragment newsRecordFragment = new NewsRecordFragment(newsRecord);
@ -143,98 +155,111 @@ implements NavigationDrawerFragment.NavigationDrawerCallbacks {
public void SetupDrawer() public void SetupDrawer()
{ {
mNavigationDrawerFragment = (NavigationDrawerFragment) getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
mNavigationDrawerFragment = (NavigationDrawerFragment)
getSupportFragmentManager().findFragmentById(R.id.navigation_drawer);
if (mNavigationDrawerFragment != null) if (mNavigationDrawerFragment != null)
{ {
// Set up the drawer. // Set up the drawer.
mNavigationDrawerFragment.setUp( mNavigationDrawerFragment.setUp(R.id.navigation_drawer, (DrawerLayout) findViewById(R.id.drawer_layout));
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();
}
} }

View File

@ -47,7 +47,7 @@ public class MainPageFragment extends Fragment implements NewsUpdatedCallbackInt
ListView listView; ListView listView;
View header; View header;
NewsManager.RemoveCallbackInterface downloadNewsCanceller; NewsManager.RemoveCallbackInterface downloadNewsCanceller = null;
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
@ -84,7 +84,11 @@ public class MainPageFragment extends Fragment implements NewsUpdatedCallbackInt
@Override @Override
public void onDestroyView() public void onDestroyView()
{ {
downloadNewsCanceller.RemoveCallback(); if (downloadNewsCanceller != null)
{
downloadNewsCanceller.RemoveCallback();
downloadNewsCanceller = null;
}
super.onDestroyView(); super.onDestroyView();
} }

View File

@ -3,9 +3,11 @@ package fishrungames.bashgid;
import android.support.v7.app.ActionBarActivity; import android.support.v7.app.ActionBarActivity;
import android.app.Activity; import android.app.Activity;
import android.app.SearchManager;
import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBar;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.SearchView;
import android.support.v4.view.GravityCompat; import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout; import android.support.v4.widget.DrawerLayout;
import android.content.Context; import android.content.Context;
@ -258,10 +260,11 @@ public class NavigationDrawerFragment extends Fragment {
// showGlobalContextActionBar, which controls the top-left area of the action bar. // showGlobalContextActionBar, which controls the top-left area of the action bar.
inflater.inflate(R.menu.global, menu); //inflater.inflate(R.menu.global, menu);
showGlobalContextActionBar(); showGlobalContextActionBar();
super.onCreateOptionsMenu(menu, inflater); super.onCreateOptionsMenu(menu, inflater);
} }
@Override @Override
@ -272,6 +275,11 @@ public class NavigationDrawerFragment extends Fragment {
return true; return true;
} }
if (item.getItemId() == R.id.action_search) {
return true;
}
if (item.getItemId() == R.id.action_settings) { if (item.getItemId() == R.id.action_settings) {
Toast.makeText(getActivity(), "Example action.", Toast.LENGTH_SHORT).show(); Toast.makeText(getActivity(), "Example action.", Toast.LENGTH_SHORT).show();
return true; return true;

View File

@ -31,7 +31,7 @@ public class NewsListFragment extends Fragment implements NewsUpdatedCallbackInt
ArrayList<NewsRecord> newsRecordArr; ArrayList<NewsRecord> newsRecordArr;
NewsManager.RemoveCallbackInterface downloadNewsCanceller; NewsManager.RemoveCallbackInterface downloadNewsCanceller = null;
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
@ -57,7 +57,11 @@ public class NewsListFragment extends Fragment implements NewsUpdatedCallbackInt
@Override @Override
public void onDestroyView() public void onDestroyView()
{ {
downloadNewsCanceller.RemoveCallback(); if (downloadNewsCanceller != null)
{
downloadNewsCanceller.RemoveCallback();
downloadNewsCanceller = null;
}
super.onDestroyView(); super.onDestroyView();
} }

View File

@ -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;
}
}

View File

@ -5,6 +5,8 @@ import java.io.FileOutputStream;
import java.io.InputStream; import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.concurrent.Semaphore;
import fishrungames.bashgid.MainActivity; import fishrungames.bashgid.MainActivity;
import fishrungames.bashgid.R; import fishrungames.bashgid.R;
@ -20,6 +22,10 @@ public class ImageManager {
static ImageManager instance = null; static ImageManager instance = null;
private volatile Semaphore downloadingFileNameSetMutex = new Semaphore(1, true);
private volatile HashSet<String> downloadingFileNameSet = new HashSet<String>();
public static ImageManager getInstance() public static ImageManager getInstance()
{ {
if (instance == null) if (instance == null)
@ -43,6 +49,8 @@ public class ImageManager {
public void ApplyImageToImageView(ImageView imageView, String imageId) public void ApplyImageToImageView(ImageView imageView, String imageId)
{ {
//Xperimental -- Should be called only in main thread
if (imageId.startsWith("R.drawable.")) if (imageId.startsWith("R.drawable."))
{ {
Integer resourceId = PredefinedImageId.get(imageId); Integer resourceId = PredefinedImageId.get(imageId);
@ -64,14 +72,40 @@ public class ImageManager {
if (f.exists()) 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 else
{ {
Log.e("Error: ", "Image requested but not downloaded: " + imageId);
imageView.setImageResource(R.drawable.transparent); imageView.setImageResource(R.drawable.transparent);
} }
} }
@ -97,26 +131,76 @@ public class ImageManager {
return; return;
} }
try { boolean fileIsBusy = false;
InputStream is = (InputStream) new URL(imageUrl).getContent();
FileOutputStream out = null;
out = MainActivity.getInstance().openFileOutput(fileName, Context.MODE_PRIVATE); 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");
}
byte [] buf = new byte[1024]; if (!fileIsBusy)
{
int numRead; try {
InputStream is = (InputStream) new URL(imageUrl).getContent();
while ( (numRead = is.read(buf) ) >= 0) { FileOutputStream out = null;
out.write(buf, 0, numRead);
}
out.close(); out = MainActivity.getInstance().openFileOutput(fileName, Context.MODE_PRIVATE);
} catch (Exception e) { byte [] buf = new byte[1024];
e.printStackTrace();
} int numRead;
while ( (numRead = is.read(buf) ) >= 0) {
out.write(buf, 0, numRead);
}
out.close();
} catch (Exception e) {
e.printStackTrace();
}
try
{
downloadingFileNameSetMutex.acquire();
try
{
downloadingFileNameSet.remove(fileName);
} finally
{
downloadingFileNameSetMutex.release();
}
} catch (InterruptedException ie)
{
Log.e("Error in DownloadImageIfNeeded", "Error in DownloadImageIfNeeded");
}
}
} }
public String GetImageLocalUrl(String imageUrl) public String GetImageLocalUrl(String imageUrl)

View File

@ -4,16 +4,38 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.Date; import java.util.Date;
import java.util.concurrent.Semaphore;
import android.util.Log;
import fishrungames.bashgid.MainActivity; import fishrungames.bashgid.MainActivity;
import fishrungames.bashgid.core.db.BashgidSqliteHelper;
public class NewsManager { public class NewsManager {
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 NewsManager instance = null;
//Xperimental -- need do something with this. Change to semaphore? private static volatile boolean SynchronizationInProcess = false; //Not strict, so don't need mutex
public static volatile boolean SynchronizationInProcess = false;
private volatile Semaphore newsRecordMutex = new Semaphore(1, true);
private ArrayList<NewsRecord> newsRecord = new ArrayList<NewsRecord>();
public static boolean CanCallSynchronization()
{
return SynchronizationInProcess;
}
public static NewsManager GetInstance() public static NewsManager GetInstance()
@ -60,16 +82,18 @@ public class NewsManager {
} }
} }
ArrayList<NewsRecord> newsRecord = new ArrayList<NewsRecord>();
public RemoveCallbackInterface DownloadNews(NewsUpdatedCallbackInterface callback) public RemoveCallbackInterface DownloadNews(NewsUpdatedCallbackInterface callback)
{ {
DownloadNewsRunnable downloadNewsRunnable = null;
if (CanCallSynchronization())
{
downloadNewsRunnable = new DownloadNewsRunnable(callback);
DownloadNewsRunnable downloadNewsRunnable = new DownloadNewsRunnable(callback); Thread thread = new Thread(downloadNewsRunnable);
Thread thread = new Thread(downloadNewsRunnable); thread.start();
}
thread.start();
return downloadNewsRunnable; return downloadNewsRunnable;
} }
@ -77,37 +101,59 @@ public class NewsManager {
class DownloadNewsRunnable implements Runnable, RemoveCallbackInterface class DownloadNewsRunnable implements Runnable, RemoveCallbackInterface
{ {
NewsUpdatedCallbackInterface callback; private volatile Semaphore callbackMutex = new Semaphore(1, true);
NewsUpdatedCallbackInterface callback = null;
public DownloadNewsRunnable(NewsUpdatedCallbackInterface callback) 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 @Override
public void RemoveCallback() 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 @Override
public void run() public void run()
{ {
if (SynchronizationInProcess)
{
return;
}
SynchronizationInProcess = true; 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/"
};
ArrayList<NewsRecord> localNewsRecord = MainActivity.getInstance().newsDataSource.getNews(); ArrayList<NewsRecord> localNewsRecord = MainActivity.getInstance().newsDataSource.getNews();
@ -118,29 +164,60 @@ public class NewsManager {
LoadNewsAndImagesFromRss2(urlArr[i], localNewsRecord, imageToDownloadList); 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) for (String imageToDownload : imageToDownloadList)
{ {
ImageManager.getInstance().DownloadImageIfNeeded(imageToDownload); ImageManager.getInstance().DownloadImageIfNeeded(imageToDownload);
} }
SynchronizationInProcess = false; SynchronizationInProcess = false;
} }
} }
@ -157,14 +234,46 @@ public class NewsManager {
public 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<NewsRecord> getNews() public ArrayList<NewsRecord> getNews()
{ {
return newsRecord; ArrayList<NewsRecord> result = new ArrayList<NewsRecord>();
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;
} }

View File

@ -7,6 +7,7 @@ import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.DOMException;
import org.w3c.dom.Document; import org.w3c.dom.Document;
import org.w3c.dom.Element; import org.w3c.dom.Element;
import org.w3c.dom.Node; import org.w3c.dom.Node;
@ -36,6 +37,11 @@ public class XmlProcessor
{ {
Log.e("Error: ", e.getMessage()); Log.e("Error: ", e.getMessage());
return null; return null;
} catch (DOMException e)
{
//Xperimental -- need to learn more about this exception
Log.e("Error: ", e.getMessage());
return null;
} catch (SAXException e) } catch (SAXException e)
{ {
Log.e("Error: ", e.getMessage()); Log.e("Error: ", e.getMessage());

View File

@ -4,6 +4,7 @@ import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Locale; import java.util.Locale;
import java.util.concurrent.Semaphore;
import fishrungames.bashgid.core.NewsManager; import fishrungames.bashgid.core.NewsManager;
@ -12,6 +13,7 @@ import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.database.SQLException; import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
public class NewsDataSource public class NewsDataSource
{ {
@ -19,25 +21,82 @@ public class NewsDataSource
//Xperimental -- move somewhere //Xperimental -- move somewhere
public static final SimpleDateFormat iso8601Format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US); 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, private String[] allColumns = { BashgidSqliteHelper.COLUMN_ID, BashgidSqliteHelper.COLUMN_TITLE, BashgidSqliteHelper.COLUMN_DESCRIPTION,
BashgidSqliteHelper.COLUMN_IMAGE_ID, BashgidSqliteHelper.COLUMN_PUBDATE }; BashgidSqliteHelper.COLUMN_IMAGE_ID, BashgidSqliteHelper.COLUMN_PUBDATE };
public NewsDataSource(Context context) {
dbHelper = new BashgidSqliteHelper(context); private final Semaphore mutex = new Semaphore(1, true);
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 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() 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) 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_IMAGE_ID, newsRecord.imageId);
values.put(BashgidSqliteHelper.COLUMN_PUBDATE, iso8601Format.format(newsRecord.pubDate)); 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<NewsManager.NewsRecord> newsRecordArr) public void replaceNews(ArrayList<NewsManager.NewsRecord> 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) for (NewsManager.NewsRecord newsRecord : newsRecordArr)
{ {
@ -70,18 +167,39 @@ public class NewsDataSource
{ {
ArrayList<NewsManager.NewsRecord> newsRecordArr = new ArrayList<NewsManager.NewsRecord>(); ArrayList<NewsManager.NewsRecord> newsRecordArr = new ArrayList<NewsManager.NewsRecord>();
/*
Cursor cursor = database.query(BashgidSqliteHelper.TABLE_NEWS, allColumns, null, null, null, null, null); try
cursor.moveToFirst();
while (!cursor.isAfterLast())
{ {
NewsManager.NewsRecord newsRecord = cursorToNewsRecord(cursor); mutex.acquire();
newsRecordArr.add(newsRecord); try
cursor.moveToNext(); {
*/
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; return newsRecordArr;
} }