Reimplementing CrytpoNews in Native Android (UPDATE #2 : ADDED SETTINGS CUSTOMIZATION)
@princessdharmy and I decided to build a native version of CryptoNews originally started by @johnesan in xamarin(which was discontinued due to being a cross platform framework, it comes with so many limitations that makes the application unable to fully harness the power of native functionalities).
History
The aim of the application is to fetch news feed from preferred blogs and display on the application. it is structured in such a way that you can scroll through them like you would do on your social media timeline. It also leverages you the ability to read any of these news
Updates and New Features
- User can now test a Website before Saving it
Before this update, this feature was not available. A User can can now Test a website if he wishes to use a different website other than the default 4 we provided.
- How it was implemented
First I Created a PreferenceUtils
Class to hold title and Url, I used SharedPreferences to save this Url as saving them to a database was not entirely necessary for this case.
public class PreferenceUtils {
private SharedPreferences mSharedPreferences;
private SharedPreferences mSharedPreferencesTest;
public static final String FIRST_URL = "first_url";
public static final String SECOND_URL = "second_url";
public static final String THIRD_URL = "third_url";
public static final String FOURTH_URL = "fourth_url";
public static final String TEST_URL = "test_url";
public static final String FIRST_TITLE = "first_title";
public static final String SECOND_TITLE = "second_title";
public static final String THIRD_TITLE = "third_title";
public static final String FOURTH_TITLE = "fourth_title";
public static final String URLCHANGED = "url_change";
public static final String NEWS_WAS_STORED = "news_was_Stored";
@Inject
public PreferenceUtils(Context context) {
mSharedPreferences = context.getSharedPreferences("cryptoNews_key",
Context.MODE_PRIVATE);
mSharedPreferencesTest = PreferenceManager.getDefaultSharedPreferences(context);
}
public void storeFirstUrl(String url) {
SharedPreferences.Editor editor = mSharedPreferences.edit();
editor.putString(FIRST_URL, url);
editor.apply();
}
public void storeFirstTitle(String url) {
SharedPreferences.Editor editor = mSharedPreferences.edit();
editor.putString(FIRST_TITLE, url);
editor.apply();
}
public String getFirstTitle (){
return mSharedPreferences.getString(FIRST_TITLE, "CCN");
}
public String getFirstUrl(){
return mSharedPreferences.getString(FIRST_URL, "https://ccn.com");
}
..........Complete Code in Repo ..............
}
Secondly I provided a separate Retrofit service to make the Api Call to see if the desired url returned the Appropriate data. As you can notice the testUrl is passed to the method and the cal is made in CheckValidUrl
class.
public class TestWebServiceNoDagger {
public static TestApiService provideTestWebService(String testUrl){
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl(testUrl)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.client(providesOkHttpClientBuilder());
return builder.build().create(TestApiService.class);
}
}
public class CheckValidUrlRepository {
PreferenceUtils preferenceUtils = new PreferenceUtils(AppController.getContextInstance());
TestApiService newsApiService = TestWebServiceNoDagger.
provideTestWebService(preferenceUtils.getTestUrl());
boolean isValidUrl;
public CheckValidUrlRepository() {
}
public LiveData<List<News>> isValidUrl(){
MutableLiveData<List<News>> newsMutableLiveData = new MutableLiveData<>();
newsApiService.getLatestNews()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<News>>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(List<News> news) {
newsMutableLiveData.setValue(news);
}
@Override
public void onError(Throwable e) {
newsMutableLiveData.setValue(null);
}
@Override
public void onComplete() {
Log.e("TAG", "onComplete was reached");
}
});
return newsMutableLiveData;
}
}
In the Fragment i am checking too see if the data returned from the livedata is null. If its null the testurl failed the test and the user is not allowed to save the Url.
We didnt use Dagger to provide the instances of the repository class because we want to be abe to change the data during runtime without restarting the activity.
- User Can save Website
The user can easily save the new url to the Sharedpreferences and is done here
//Save the new data to the appropriate preference
binding.get().saveButton.setOnClickListener(view -> {
savedItem = true;
listener.onBackPressed(savedItem);
switch (value){
case FIRST_URL:
preferenceUtils.storeFirstUrl(binding.get().websiteUrl.getText().toString());
preferenceUtils.storeFirstTitle(binding.get().websiteTitle.getText().toString());
break;
case SECOND_URL:
preferenceUtils.storeSecondTitle(binding.get().websiteUrl.getText().toString());
preferenceUtils.storeSecondUrl(binding.get().websiteTitle.getText().toString());
break;
case THIRD_URL:
preferenceUtils.storeThirdUrl(binding.get().websiteUrl.getText().toString());
preferenceUtils.storeThirdTitle(binding.get().websiteTitle.getText().toString());
break;
case FOURTH_URL:
preferenceUtils.storeFourthUrl(binding.get().websiteUrl.getText().toString());
preferenceUtils.storeFourthTitle(binding.get().websiteTitle.getText().toString());
break;
default:
break;
}
- User Can Choose What Font Size to use
We want to give the User a complete control on how the news appears on the app so we added a Font Size Feature. This is Experimental because the Ui may be a little distorted as it still needs a little tweaking.
How i was Implemented
Once the user Selects a Font size in the settings and returns to the MainActivity, i placed the code to check if the user has changed the font in the onResume Method as this method is being called first according to Android LifeCycle Processes.
The code is shown below
@Override
public void onResume() {
super.onResume();
//refresh if font size Changed
if(refreshFontSize()){
if (mAdapter == null) {
mAdapter = new NewsAdapter(getContext());
}
mAdapter.setFontSizes(mFontSizeTitle, mFontSizeDetails);
mAdapter.notifyDataSetChanged();
// Toast.makeText(getContext(), preferenceUtils.getFontSize(), Toast.LENGTH_SHORT).show();
}
}
If the check passes, the adapter is notified, and the font size is set automatically. Here is the method for the font sizes.
private boolean refreshFontSize() {
final String fontSize = preferenceUtils.getFontSize();
if ((mCurrentFontSize == null || (!mCurrentFontSize.equals(fontSize)))){
mCurrentFontSize = fontSize;
if (fontSize.equals("0")){
mFontSizeTitle = 10;
mFontSizeDetails = 9;
}else if (fontSize.equals("1"))
{mFontSizeTitle = 13;
mFontSizeDetails = 10;}
else {
mFontSizeTitle = 14;
mFontSizeDetails = 11;
}
return true;
}else {
return false;
}
}
In the adapter this line of code is added to set the Font size
holder.date.setTextSize(TypedValue.COMPLEX_UNIT_DIP, mFontSizeDetails);
- User Can choose to read Article inApp or through Favorite Browser
We want to be able to give a user the opportunity choose where the News should be read from since different browsers renders pages differently. A user may prefer another way.
How it was implemented
We implement this in the NewsAdapter
in the onClick
method.
we checked if the user preference in the settings was In App
or Web Browsers
then we take the user to where the preferred choice.
This is shown below.
@Override
public void onClick(View v) {
Context context = v.getContext();
Intent intent = new Intent(context, NewsWebPageActivity.class);
News data = newsList.get(getLayoutPosition());
//Get the link of the website to be opened on the Web page
String link = data.getGuid().getRendered();
//Get the title of each news and format it to normal characters
String title = String.valueOf(Html.fromHtml(data.getTitle().getRendered()));
if (!preferenceUtils.getViewNewsWithIn().equals("0")){
openURLInBrowser(link);
}else {
//Pass the title and link to the next activity
intent.putExtra("url", link);
intent.putExtra("title", title);
context.startActivity(intent);
}
}
private void openURLInBrowser(String url) {
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
context.startActivity(browserIntent);
}
}
The OpenURLInBrowser()
method takes the user to action screen where he selects a browser to use.
Roadmap
- Searching all news
Persisting user news- Providing different layouts
- Providing different themes for user
- Push Notifications
- News Posts sharing
- Incorporating more news and giving the user the flexibility of deciding what he wants to read per time.
Resources
Posted on Utopian.io - Rewarding Open Source Contributors
Thank you for the contribution. It has been approved.
Great post, very descriptive! Keep up the good work!
You can contact us on Discord.
[utopian-moderator]
Hey @mathemandy I am @utopian-io. I have just upvoted you!
Achievements
Community-Driven Witness!
I am the first and only Steem Community-Driven Witness. Participate on Discord. Lets GROW TOGETHER!
Up-vote this comment to grow my power and help Open Source contributions like this one. Want to chat? Join me on Discord https://discord.gg/Pc8HG9x