From 40f0626fb053a5abe6d3692b36409f7f262ce023 Mon Sep 17 00:00:00 2001 From: d954mas Date: Sun, 24 Jul 2016 22:42:08 +0300 Subject: [PATCH 01/16] load artist data --- app/src/main/java/ru/yandex/yamblz/App.java | 2 + .../ru/yandex/yamblz/artists/ArtistModel.java | 63 +++++++ .../yamblz/artists/DataLoadingFragment.java | 25 +++ .../yamblz/artists/DataLoadingModel.java | 156 ++++++++++++++++++ .../artists/DefaultLoadingObserver.java | 47 ++++++ .../yamblz/artists/utils/CacheHelper.java | 70 ++++++++ .../yamblz/artists/utils/DataSingleton.java | 101 ++++++++++++ .../yamblz/ui/activities/MainActivity.java | 16 +- .../ui/fragments/InternetErrorFragment.java | 22 +++ .../yamblz/ui/fragments/LoadingFragment.java | 74 +++++++++ .../res/layout/internet_error_fragment.xml | 27 +++ app/src/main/res/values/strings.xml | 2 + dependencies.gradle | 2 +- 13 files changed, 601 insertions(+), 6 deletions(-) create mode 100644 app/src/main/java/ru/yandex/yamblz/artists/ArtistModel.java create mode 100644 app/src/main/java/ru/yandex/yamblz/artists/DataLoadingFragment.java create mode 100644 app/src/main/java/ru/yandex/yamblz/artists/DataLoadingModel.java create mode 100644 app/src/main/java/ru/yandex/yamblz/artists/DefaultLoadingObserver.java create mode 100644 app/src/main/java/ru/yandex/yamblz/artists/utils/CacheHelper.java create mode 100644 app/src/main/java/ru/yandex/yamblz/artists/utils/DataSingleton.java create mode 100644 app/src/main/java/ru/yandex/yamblz/ui/fragments/InternetErrorFragment.java create mode 100644 app/src/main/java/ru/yandex/yamblz/ui/fragments/LoadingFragment.java create mode 100644 app/src/main/res/layout/internet_error_fragment.xml diff --git a/app/src/main/java/ru/yandex/yamblz/App.java b/app/src/main/java/ru/yandex/yamblz/App.java index e5f9972..7ee5b90 100644 --- a/app/src/main/java/ru/yandex/yamblz/App.java +++ b/app/src/main/java/ru/yandex/yamblz/App.java @@ -4,6 +4,7 @@ import android.content.Context; import android.support.annotation.NonNull; +import ru.yandex.yamblz.artists.utils.DataSingleton; import ru.yandex.yamblz.developer_settings.DevMetricsProxy; import ru.yandex.yamblz.developer_settings.DeveloperSettingsModel; import ru.yandex.yamblz.handler.CriticalSectionsManager; @@ -36,6 +37,7 @@ public void onCreate() { CollageLoaderManager.init(null); // add implementation CriticalSectionsManager.init(null); // add implementation + DataSingleton.init(this); } @NonNull diff --git a/app/src/main/java/ru/yandex/yamblz/artists/ArtistModel.java b/app/src/main/java/ru/yandex/yamblz/artists/ArtistModel.java new file mode 100644 index 0000000..816918e --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/artists/ArtistModel.java @@ -0,0 +1,63 @@ +package ru.yandex.yamblz.artists; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; + +//Модель артиста,генерируется из jsonObject +public class ArtistModel { + protected JSONObject json; + public long id; + public String name; + public String smallImageUrl; + public String bigImageUrl; + public ArrayList genres; + public int tracks; + public int albums; + public String link; + public String description; + + public ArtistModel(){ + genres=new ArrayList<>(); + } + + public ArtistModel(JSONObject artist){ + this(); + changeData(artist); + } + + public void changeData(JSONObject json){ + this.json = json; + genres.clear(); + try { + id=json.getLong("id"); + name=json.getString("name"); + JSONArray genresJsonArray=json.getJSONArray("genres"); + for (int i = 0; i < genresJsonArray.length(); i++) { + genres.add(genresJsonArray.getString(i)); + } + smallImageUrl=json.getJSONObject("cover").getString("small"); + bigImageUrl=json.getJSONObject("cover").getString("big"); + tracks=json.getInt("tracks"); + albums=json.getInt("albums"); + if(json.has("link")){ + link=json.getString("link"); + }else{ + link=""; + } + + description=json.getString("description"); + if(!description.isEmpty()){//игнорируем пустые строки + description=Character.toUpperCase(description.charAt(0))+description.substring(1,description.length()); + } + } catch (JSONException e) { + e.printStackTrace(); + } + } + + public JSONObject getJson() { + return json; + } +} diff --git a/app/src/main/java/ru/yandex/yamblz/artists/DataLoadingFragment.java b/app/src/main/java/ru/yandex/yamblz/artists/DataLoadingFragment.java new file mode 100644 index 0000000..4f499ba --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/artists/DataLoadingFragment.java @@ -0,0 +1,25 @@ +package ru.yandex.yamblz.artists; + +import android.os.Bundle; +import android.support.v4.app.Fragment; + + +//retain fragment для загрузки данные,не имеет ui,нужен только +//для сохранения и доступа к DataLoadingModel +public class DataLoadingFragment extends Fragment { + private final DataLoadingModel dataLoadingModel; + + public DataLoadingFragment() { + dataLoadingModel = new DataLoadingModel(); + } + + @Override + public void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setRetainInstance(true); + } + + public DataLoadingModel getDataLoadingModel() { + return dataLoadingModel; + } +} diff --git a/app/src/main/java/ru/yandex/yamblz/artists/DataLoadingModel.java b/app/src/main/java/ru/yandex/yamblz/artists/DataLoadingModel.java new file mode 100644 index 0000000..6dfb56c --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/artists/DataLoadingModel.java @@ -0,0 +1,156 @@ +package ru.yandex.yamblz.artists; + +import android.database.Observable; +import android.os.AsyncTask; +import android.util.Log; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; + +import ru.yandex.yamblz.artists.utils.DataSingleton; + +/*модель для асинхронной загрузки данных. +Можно подписатся на события:начала загрузки,ошибки,успеха +//при добавление слушателя,если данные сенйчас грузятся, +у слушателя вызавется onLoadingStart + */ +public class DataLoadingModel { + + + private static final String TAG = "DataLoadingModel"; + + private final DataLoadingObservable mObservable = new DataLoadingObservable(); + private LoadAsyncTask loadingTask; + private boolean isWorking; + + public DataLoadingModel() { + } + + public void loadData() { + if (isWorking) { + return; + } + mObservable.notifyStarted(); + isWorking = true; + loadingTask = new LoadAsyncTask(); + loadingTask.execute(); + } + + public void stopLoadData() { + if (isWorking) { + loadingTask.cancel(true); + isWorking = false; + } + } + + public void registerObserver(final Observer observer) { + mObservable.registerObserver(observer); + if (isWorking) { + observer.onLoadingStart(this); + } + } + + public void unregisterObserver(final Observer observer) { + mObservable.unregisterObserver(observer); + } + + public interface Observer { + void onLoadingStart(DataLoadingModel signInModel); + + void onLoadingSucceeded(DataLoadingModel signInModel); + + void onLoadingFailed(DataLoadingModel signInModel); + } + + private class DataLoadingObservable extends Observable { + public void notifyStarted() { + for (final Observer observer : mObservers) { + observer.onLoadingStart(DataLoadingModel.this); + } + } + + public void notifySucceeded() { + for (final Observer observer : mObservers) { + observer.onLoadingSucceeded(DataLoadingModel.this); + } + } + + public void notifyFailed() { + for (final Observer observer : mObservers) { + observer.onLoadingFailed(DataLoadingModel.this); + } + } + } + private class LoadAsyncTask extends AsyncTask { + + protected String loadArtistsFromWeb() { + URL url = null; + BufferedReader reader = null; + String result=null; + try { + url = new URL("http://download.cdn.yandex.net/mobilization-2016/artists.json"); + HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); + urlConnection.setRequestMethod("GET"); + urlConnection.connect(); + InputStream inputStream = urlConnection.getInputStream(); + StringBuilder buffer = new StringBuilder(); + reader = new BufferedReader(new InputStreamReader(inputStream)); + String line; + while ((line = reader.readLine()) != null) { + buffer.append(line); + } + result=buffer.toString(); + } catch (IOException e) { + e.printStackTrace(); + } + finally { + if(reader!=null) try { + reader.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + return result; + + } + + protected boolean readArtistJson() { + if (DataSingleton.get().hasData()) { + Log.i(TAG, "already loaded"); + return true; + } else { + String jsonString = loadArtistsFromWeb(); + if (jsonString == null) { + Log.i(TAG, "failed to load data"); + return false; + } else { + DataSingleton.get().setData(jsonString); + return true; + } + } + + } + + @Override + protected Boolean doInBackground(Void... voids) { + return readArtistJson(); + } + + @Override + protected void onPostExecute(Boolean success) { + super.onPostExecute(success); + isWorking = false; + if (success) { + Log.i(TAG,"successfully get data"); + mObservable.notifySucceeded(); + } else { + Log.i(TAG,"error while get data"); + mObservable.notifyFailed(); + } + } + } +} diff --git a/app/src/main/java/ru/yandex/yamblz/artists/DefaultLoadingObserver.java b/app/src/main/java/ru/yandex/yamblz/artists/DefaultLoadingObserver.java new file mode 100644 index 0000000..30481db --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/artists/DefaultLoadingObserver.java @@ -0,0 +1,47 @@ +package ru.yandex.yamblz.artists; + +import android.app.ProgressDialog; +import android.content.Context; +import android.util.Log; + +public class DefaultLoadingObserver implements DataLoadingModel.Observer { + + private ProgressDialog progressDialog; + private Context context; + private static final String TAG = "DefaultLoadingObserver"; + + public DefaultLoadingObserver(Context context){ + this.context=context; + } + + @Override + public void onLoadingStart(DataLoadingModel loadingModel) { + if(progressDialog==null){ + progressDialog = new ProgressDialog(context); + } + progressDialog.setCancelable(false); + progressDialog.setMessage("Loading..."); + progressDialog.show(); + Log.i(TAG, "start getting data"); + } + + @Override + public void onLoadingSucceeded(DataLoadingModel loadingModel) { + Log.i(TAG, "successfully get data"); + progressDialog.dismiss(); + dispose(); + + } + + @Override + public void onLoadingFailed(DataLoadingModel loadingModel) { + progressDialog.dismiss(); + Log.i(TAG, "failed get data"); + dispose(); + } + + public void dispose(){ + if(progressDialog!=null)progressDialog.dismiss(); + } + +} diff --git a/app/src/main/java/ru/yandex/yamblz/artists/utils/CacheHelper.java b/app/src/main/java/ru/yandex/yamblz/artists/utils/CacheHelper.java new file mode 100644 index 0000000..be1cdea --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/artists/utils/CacheHelper.java @@ -0,0 +1,70 @@ +package ru.yandex.yamblz.artists.utils; + +import android.content.Context; +import android.util.Log; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileWriter; +import java.io.IOException; + +//класс для сохранения,получения и удаления строк в файл(кеширования) +public class CacheHelper { + private static final String TAG="CacheHelper"; + private static final String directory="/CachedStrings/"; + + public static void cacheString(Context context,String key,String mJsonResponse) { + try { + File checkFile = new File(context.getApplicationInfo().dataDir + directory); + if (!checkFile.exists()) { + checkFile.mkdir(); + } + FileWriter file = new FileWriter(checkFile.getAbsolutePath()+ key); + file.write(mJsonResponse); + file.flush(); + file.close(); + Log.i(TAG,"cache write key:"+key); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static String readCacheString(Context context,String key) { + try { + File checkFile = new File(context.getApplicationInfo().dataDir + directory); + File f = new File(checkFile.getAbsolutePath()+ key); + if (!f.exists()) { + return null; + } + FileInputStream is = new FileInputStream(f); + int size = is.available(); + byte[] buffer = new byte[size]; + is.read(buffer); + is.close(); + Log.i(TAG, "cache read key:" + key); + return new String(buffer); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + public static void clearCache(Context context) { + File checkFile = new File(context.getApplicationInfo().dataDir + directory); + File f = new File(checkFile.getAbsolutePath()); + File[] files = f.listFiles(); + for (File fInDir : files) { + fInDir.delete(); + } + Log.i(TAG,"clear cache"); + } + + public static void deleteFile(Context context,String key) { + File checkFile = new File(context.getApplicationInfo().dataDir + directory); + File f = new File(checkFile.getAbsolutePath()+ key); + if (f.exists()) { + f.delete(); + } + Log.i(TAG,"delete cache key:"+key); + } +} diff --git a/app/src/main/java/ru/yandex/yamblz/artists/utils/DataSingleton.java b/app/src/main/java/ru/yandex/yamblz/artists/utils/DataSingleton.java new file mode 100644 index 0000000..0466fbf --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/artists/utils/DataSingleton.java @@ -0,0 +1,101 @@ +package ru.yandex.yamblz.artists.utils; + +import android.content.Context; +import android.util.Log; + +import org.json.JSONArray; +import org.json.JSONException; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +import ru.yandex.yamblz.artists.ArtistModel; + +//Singleton для получения данных об артистах из любого места приложения + +public class DataSingleton { + private static final String TAG="DataSingleton"; + private static final String ARTIST_JSON_KEY = "cachedArtists"; + private static DataSingleton dataSingleton; + private Context context; + private List artists; + private HashMap> artistsByGenre; + + private DataSingleton(Context context){ + this.context=context; + String jsonString=CacheHelper.readCacheString(context, ARTIST_JSON_KEY); + if(false && jsonString!=null){ + artists=parseData(jsonString); + Log.i(TAG,"get data from cache"); + } + } + + private List parseData(String json){ + CacheHelper.cacheString(context, ARTIST_JSON_KEY,json); + Log.i(TAG, "save artists to cache"); + + try { + List artist = new ArrayList<>(); + JSONArray artistList = new JSONArray(json); + artistsByGenre=new HashMap<>(); + for (int i = 0; i < artistList.length(); i++) { + ArtistModel artistModel=new ArtistModel(artistList.getJSONObject(i)); + artist.add(artistModel); + for(String genre:artistModel.genres){ + List artistGenreList; + if(artistsByGenre.containsKey(genre)){ + artistGenreList=artistsByGenre.get(genre); + }else{ + artistGenreList=new ArrayList<>(); + artistsByGenre.put(genre,artistGenreList); + } + artistGenreList.add(artistModel); + } + } + Collections.sort(artist, (lhs, rhs) -> lhs.name.compareTo(rhs.name)); + return artist; + + } catch (JSONException e) { + e.printStackTrace(); + throw new RuntimeException("bad json data"); + } + } + + public static void init(Context context){ + if(dataSingleton!=null){ + throw new RuntimeException("singleton must be init once"); + }else{ + dataSingleton=new DataSingleton(context); + } + } + + public void setData(String json){ + artists=parseData(json); + } + + public boolean hasData(){ + return false && artists!=null; + } + + public List getArtists() { + return artists; + } + + public static DataSingleton get(){ + return dataSingleton; + } + + public static void dispose(){ + dataSingleton=null; + } + + public List getGenres() { + return new ArrayList<>(artistsByGenre.keySet()); + } + + public List getArtistsByGenre(String genre){ + return artistsByGenre.get(genre); + } +} diff --git a/app/src/main/java/ru/yandex/yamblz/ui/activities/MainActivity.java b/app/src/main/java/ru/yandex/yamblz/ui/activities/MainActivity.java index 3b9efea..8cba3b8 100644 --- a/app/src/main/java/ru/yandex/yamblz/ui/activities/MainActivity.java +++ b/app/src/main/java/ru/yandex/yamblz/ui/activities/MainActivity.java @@ -9,8 +9,9 @@ import ru.yandex.yamblz.App; import ru.yandex.yamblz.R; +import ru.yandex.yamblz.artists.utils.DataSingleton; import ru.yandex.yamblz.developer_settings.DeveloperSettingsModule; -import ru.yandex.yamblz.ui.fragments.ContentFragment; +import ru.yandex.yamblz.ui.fragments.LoadingFragment; import ru.yandex.yamblz.ui.other.ViewModifier; public class MainActivity extends BaseActivity { @@ -27,10 +28,15 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { setContentView(viewModifier.modify(getLayoutInflater().inflate(R.layout.activity_main, null))); if (savedInstanceState == null) { - getSupportFragmentManager() - .beginTransaction() - .replace(R.id.main_frame_layout, new ContentFragment()) - .commit(); + if(DataSingleton.get().hasData()){ + + }else{ + getSupportFragmentManager() + .beginTransaction() + .replace(R.id.main_frame_layout, new LoadingFragment()) + .commit(); + } + } } } diff --git a/app/src/main/java/ru/yandex/yamblz/ui/fragments/InternetErrorFragment.java b/app/src/main/java/ru/yandex/yamblz/ui/fragments/InternetErrorFragment.java new file mode 100644 index 0000000..21fcfad --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/ui/fragments/InternetErrorFragment.java @@ -0,0 +1,22 @@ +package ru.yandex.yamblz.ui.fragments; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import ru.yandex.yamblz.R; + +//при отсутствие интернета, показываем этот фрагмент +public class InternetErrorFragment extends Fragment { + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.internet_error_fragment, container, false); + view.findViewById(R.id.reconnect_button).setOnClickListener(v -> + getActivity().getSupportFragmentManager().beginTransaction() + .replace(R.id.main_frame_layout, new LoadingFragment()).commit()); + return view; + } +} diff --git a/app/src/main/java/ru/yandex/yamblz/ui/fragments/LoadingFragment.java b/app/src/main/java/ru/yandex/yamblz/ui/fragments/LoadingFragment.java new file mode 100644 index 0000000..7298eb7 --- /dev/null +++ b/app/src/main/java/ru/yandex/yamblz/ui/fragments/LoadingFragment.java @@ -0,0 +1,74 @@ +package ru.yandex.yamblz.ui.fragments; + + +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.util.Log; + +import ru.yandex.yamblz.R; +import ru.yandex.yamblz.artists.DataLoadingFragment; +import ru.yandex.yamblz.artists.DataLoadingModel; +import ru.yandex.yamblz.artists.DefaultLoadingObserver; + +import static android.content.ContentValues.TAG; + +public class LoadingFragment extends Fragment implements DataLoadingModel.Observer { + + private DataLoadingModel dataLoadingModel; + private DefaultLoadingObserver defaultLoadingObserver; + + public void load() { + Log.d(TAG, "loading"); + String TAG_DATA_LOADING = "TAG_DATA_LOADING"; + //получаем фрагмент для загрузки данных(если он уже был создан) + final DataLoadingFragment retainDataLoadingFragment = + (DataLoadingFragment) getActivity().getSupportFragmentManager().findFragmentByTag(TAG_DATA_LOADING); + if (retainDataLoadingFragment != null) { + Log.d(TAG, "has data model"); + dataLoadingModel = retainDataLoadingFragment.getDataLoadingModel(); + } else { + Log.d(TAG, "no data model"); + final DataLoadingFragment dataLoadingFragment = new DataLoadingFragment(); + getActivity().getSupportFragmentManager().beginTransaction() + .add(dataLoadingFragment, TAG_DATA_LOADING) + .commit(); + dataLoadingModel = dataLoadingFragment.getDataLoadingModel(); + } + defaultLoadingObserver = new DefaultLoadingObserver(getContext()); + dataLoadingModel.registerObserver(defaultLoadingObserver); + dataLoadingModel.registerObserver(this); + dataLoadingModel.loadData(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + dataLoadingModel.unregisterObserver(defaultLoadingObserver); + dataLoadingModel.unregisterObserver(this); + defaultLoadingObserver.dispose(); + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + load(); + } + + @Override + public void onLoadingStart(DataLoadingModel signInModel) { + + } + + @Override + public void onLoadingSucceeded(DataLoadingModel signInModel) { + getActivity().getSupportFragmentManager().beginTransaction() + .replace(R.id.main_frame_layout,new ContentFragment()).commit(); + } + + @Override + public void onLoadingFailed(DataLoadingModel signInModel) { + getActivity().getSupportFragmentManager().beginTransaction(). + replace(R.id.main_frame_layout,new InternetErrorFragment()).commit(); + } +} diff --git a/app/src/main/res/layout/internet_error_fragment.xml b/app/src/main/res/layout/internet_error_fragment.xml new file mode 100644 index 0000000..476f4e7 --- /dev/null +++ b/app/src/main/res/layout/internet_error_fragment.xml @@ -0,0 +1,27 @@ + + + + + + +