diff --git a/app/app-fdroid-release.apk b/app/app-fdroid-release.apk new file mode 100644 index 000000000..d9e6f72e9 Binary files /dev/null and b/app/app-fdroid-release.apk differ diff --git a/app/build.gradle b/app/build.gradle index 2b6c24625..59b7089f9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,8 +7,8 @@ android { applicationId "fr.gouv.etalab.mastodon" minSdkVersion 15 targetSdkVersion 25 - versionCode 48 - versionName "1.4.8" + versionCode 49 + versionName "1.4.9-beta-1" } buildTypes { release { diff --git a/app/src/fdroid/java/fr.gouv.etalab.mastodon/activities/MainActivity.java b/app/src/fdroid/java/fr.gouv.etalab.mastodon/activities/MainActivity.java index fb0c6943d..dc8566632 100644 --- a/app/src/fdroid/java/fr.gouv.etalab.mastodon/activities/MainActivity.java +++ b/app/src/fdroid/java/fr.gouv.etalab.mastodon/activities/MainActivity.java @@ -15,26 +15,31 @@ package fr.gouv.etalab.mastodon.activities; import android.annotation.SuppressLint; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.IntentFilter; import android.content.SharedPreferences; import android.database.sqlite.SQLiteDatabase; import android.graphics.PorterDuff; import android.net.Uri; import android.os.AsyncTask; +import android.os.Build; import android.os.Bundle; -import android.os.Handler; import android.support.annotation.NonNull; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.TabLayout; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.content.ContextCompat; +import android.support.v4.content.LocalBroadcastManager; import android.support.v4.view.ViewPager; import android.support.v7.app.AlertDialog; import android.support.v7.widget.SearchView; import android.support.v7.widget.SwitchCompat; +import android.util.Log; +import android.util.Patterns; import android.view.LayoutInflater; import android.view.View; import android.support.design.widget.NavigationView; @@ -61,12 +66,13 @@ import com.nostra13.universalimageloader.core.display.RoundedBitmapDisplayer; import java.io.File; import java.util.ArrayList; -import java.util.Date; import java.util.HashMap; +import java.util.List; import java.util.Locale; import java.util.Stack; -import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import fr.gouv.etalab.mastodon.asynctasks.RetrieveMetaDataAsyncTask; import fr.gouv.etalab.mastodon.asynctasks.UpdateAccountInfoByIDAsyncTask; import fr.gouv.etalab.mastodon.client.Entities.Account; import fr.gouv.etalab.mastodon.client.PatchBaseImageDownloader; @@ -75,7 +81,9 @@ import fr.gouv.etalab.mastodon.fragments.DisplayFollowRequestSentFragment; import fr.gouv.etalab.mastodon.fragments.DisplayNotificationsFragment; import fr.gouv.etalab.mastodon.fragments.DisplayScheduledTootsFragment; import fr.gouv.etalab.mastodon.helper.Helper; +import fr.gouv.etalab.mastodon.interfaces.OnRetrieveMetaDataInterface; import fr.gouv.etalab.mastodon.interfaces.OnUpdateAccountInfoInterface; +import fr.gouv.etalab.mastodon.services.StreamingService; import fr.gouv.etalab.mastodon.sqlite.Sqlite; import fr.gouv.etalab.mastodon.asynctasks.RetrieveAccountsAsyncTask; import fr.gouv.etalab.mastodon.asynctasks.RetrieveFeedsAsyncTask; @@ -100,7 +108,7 @@ import android.support.v4.app.FragmentStatePagerAdapter; public class MainActivity extends AppCompatActivity - implements NavigationView.OnNavigationItemSelectedListener, OnUpdateAccountInfoInterface { + implements NavigationView.OnNavigationItemSelectedListener, OnUpdateAccountInfoInterface, OnRetrieveMetaDataInterface { private FloatingActionButton toot; private HashMap tagTile = new HashMap<>(); @@ -119,7 +127,8 @@ public class MainActivity extends AppCompatActivity private DisplayStatusFragment homeFragment; private DisplayNotificationsFragment notificationsFragment; - + private BroadcastReceiver receive_data; + private boolean display_local, display_global; public MainActivity() { } @@ -127,7 +136,47 @@ public class MainActivity extends AppCompatActivity @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, android.content.Context.MODE_PRIVATE); + receive_data = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + Bundle b = intent.getExtras(); + StreamingService.EventStreaming eventStreaming = (StreamingService.EventStreaming) intent.getSerializableExtra("eventStreaming"); + if( eventStreaming == StreamingService.EventStreaming.NOTIFICATION){ + if(notificationsFragment != null){ + if(notificationsFragment.getUserVisibleHint() && isActivityVisible()){ + notificationsFragment.showNewContent(); + }else{ + notificationsFragment.refresh(); + } + } + }else if(eventStreaming == StreamingService.EventStreaming.UPDATE){ + if( homeFragment != null){ + if(homeFragment.getUserVisibleHint() && isActivityVisible()){ + homeFragment.showNewContent(); + }else{ + homeFragment.refresh(); + } + } + }else if(eventStreaming == StreamingService.EventStreaming.DELETE){ + String id = b.getString("id"); + if(notificationsFragment != null) { + if (notificationsFragment.getUserVisibleHint()) { + + } else { + + } + } + } + updateNotifCounter(); + updateHomeCounter(); + } + }; + LocalBroadcastManager.getInstance(this).registerReceiver(receive_data, new IntentFilter(Helper.RECEIVE_DATA)); + + final int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK); if( theme == Helper.THEME_LIGHT){ @@ -137,6 +186,9 @@ public class MainActivity extends AppCompatActivity } setContentView(R.layout.activity_main); + display_local = sharedpreferences.getBoolean(Helper.SET_DISPLAY_LOCAL, true); + display_global = sharedpreferences.getBoolean(Helper.SET_DISPLAY_GLOBAL, true); + //Test if user is still log in if( ! Helper.isLoggedIn(getApplicationContext())) { //It is not, the user is redirected to the login page @@ -145,7 +197,16 @@ public class MainActivity extends AppCompatActivity finish(); return; } - + SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + List accounts = new AccountDAO(getApplicationContext(), db).getAllAccount(); + if( accounts != null){ + for (Account account: accounts) { + Intent intent = new Intent(getApplicationContext(), StreamingService.class); + intent.putExtra("accountId", account.getId()); + intent.putExtra("accountAcct", account.getAcct()); + startService(intent); + } + } Helper.fillMapEmoji(getApplicationContext()); //Here, the user is authenticated Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); @@ -194,8 +255,10 @@ public class MainActivity extends AppCompatActivity tabLayout.addTab(tabHome); tabLayout.addTab(tabNotif); - tabLayout.addTab(tabLocal); - tabLayout.addTab(tabPublic); + if( display_local) + tabLayout.addTab(tabLocal); + if( display_global) + tabLayout.addTab(tabPublic); viewPager = (ViewPager) findViewById(R.id.viewpager); main_app_container = (RelativeLayout) findViewById(R.id.main_app_container); @@ -203,7 +266,6 @@ public class MainActivity extends AppCompatActivity (getSupportFragmentManager(), tabLayout.getTabCount()); viewPager.setAdapter(adapter); viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout)); - final boolean bubbles = sharedpreferences.getBoolean(Helper.SET_BUBBLE_COUNTER, true); tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { @Override public void onTabSelected(TabLayout.Tab tab) { @@ -222,29 +284,32 @@ public class MainActivity extends AppCompatActivity main_app_container.setVisibility(View.GONE); viewPager.setVisibility(View.VISIBLE); Helper.switchLayout(MainActivity.this); - switch (tab.getPosition()){ - case 0: - item = navigationView.getMenu().findItem(R.id.nav_home); - fragmentTag = "HOME_TIMELINE"; - if( bubbles && homeFragment != null) - homeFragment.refreshData(); - updateHomeCounter(0); - break; - case 1: - fragmentTag = "NOTIFICATIONS"; - item = navigationView.getMenu().findItem(R.id.nav_notification); - updateNotifCounter(0); - if( bubbles && notificationsFragment != null) - notificationsFragment.refreshData(); - break; - case 2: - fragmentTag = "LOCAL_TIMELINE"; - item = navigationView.getMenu().findItem(R.id.nav_local); - break; - case 3: + if( tab.getPosition() == 0) { + item = navigationView.getMenu().findItem(R.id.nav_home); + fragmentTag = "HOME_TIMELINE"; + if (homeFragment != null && Helper.getUnreadToots(getApplicationContext(), null) > 0) { + homeFragment.refresh(); + } + Helper.cacheStatusClear(getApplicationContext(), null); + updateHomeCounter(); + }else if( tab.getPosition() == 1) { + fragmentTag = "NOTIFICATIONS"; + item = navigationView.getMenu().findItem(R.id.nav_notification); + if (notificationsFragment != null && Helper.getUnreadNotifications(getApplicationContext(), null) > 0) { + notificationsFragment.refresh(); + } + Helper.cacheNotificationsClear(getApplicationContext(), null); + updateNotifCounter(); + }else if( tab.getPosition() == 2 && display_local) { + + fragmentTag = "LOCAL_TIMELINE"; + item = navigationView.getMenu().findItem(R.id.nav_local); + }else if( tab.getPosition() == 2 && !display_local) { item = navigationView.getMenu().findItem(R.id.nav_global); fragmentTag = "PUBLIC_TIMELINE"; - break; + }else if( tab.getPosition() == 3){ + item = navigationView.getMenu().findItem(R.id.nav_global); + fragmentTag = "PUBLIC_TIMELINE"; } if( item != null){ toolbarTitle.setText(item.getTitle()); @@ -281,9 +346,15 @@ public class MainActivity extends AppCompatActivity Fragment fragment = (Fragment) viewPager.getAdapter().instantiateItem(viewPager, tab.getPosition()); switch (tab.getPosition()){ case 0: + DisplayStatusFragment displayStatusFragment = ((DisplayStatusFragment) fragment); + if( displayStatusFragment != null ) + displayStatusFragment.scrollToTop(); + Helper.cacheStatusClear(getApplicationContext(), null); + updateHomeCounter(); + break; case 2: case 3: - DisplayStatusFragment displayStatusFragment = ((DisplayStatusFragment) fragment); + displayStatusFragment = ((DisplayStatusFragment) fragment); if( displayStatusFragment != null ) displayStatusFragment.scrollToTop(); break; @@ -291,6 +362,8 @@ public class MainActivity extends AppCompatActivity DisplayNotificationsFragment displayNotificationsFragment = ((DisplayNotificationsFragment) fragment); if( displayNotificationsFragment != null ) displayNotificationsFragment.scrollToTop(); + Helper.cacheNotificationsClear(getApplicationContext(), null); + updateNotifCounter(); break; } } @@ -354,7 +427,11 @@ public class MainActivity extends AppCompatActivity Intent intent = new Intent(MainActivity.this, SearchResultActivity.class); intent.putExtra("search", query); startActivity(intent); - return true; + toolbar_search.setQuery("", false); + toolbar_search.setIconified(true); + toolbarTitle.setVisibility(View.VISIBLE); + pp_actionBar.setVisibility(View.VISIBLE); + return false; } @Override public boolean onQueryTextChange(String newText) { @@ -421,15 +498,14 @@ public class MainActivity extends AppCompatActivity .diskCache(new UnlimitedDiskCache(cacheDir)) .build(); imageLoader.init(configImg); - SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); options = new DisplayImageOptions.Builder().displayer(new RoundedBitmapDisplayer(90)).cacheInMemory(false) .cacheOnDisk(true).resetViewBeforeLoading(true).build(); headerLayout = navigationView.getHeaderView(0); - String prefKeyOauthTokenT = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null); - Account account = new AccountDAO(getApplicationContext(), db).getAccountByToken(prefKeyOauthTokenT); + String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); + Account account = new AccountDAO(getApplicationContext(), db).getAccountByID(userId); updateHeaderAccountInfo(MainActivity.this, account, headerLayout, imageLoader, options); loadPPInActionBar(MainActivity.this, account.getAvatar()); //Locked account can see follow request @@ -545,6 +621,21 @@ public class MainActivity extends AppCompatActivity String sharedSubject = intent.getStringExtra(Intent.EXTRA_SUBJECT); String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT); if (sharedText != null) { + /* Some apps don't send the URL as the first part of the EXTRA_TEXT, + the BBC News app being one such, in this case find where the URL + is and strip that out into sharedText. + */ + Matcher matcher; + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) + matcher = Patterns.WEB_URL.matcher(sharedText); + else + matcher = Helper.urlPattern.matcher(sharedText); + while (matcher.find()){ + int matchStart = matcher.start(1); + int matchEnd = matcher.end(); + sharedText = sharedText.substring(matchStart, matchEnd); + } + new RetrieveMetaDataAsyncTask(sharedText, MainActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); Intent intentToot = new Intent(getApplicationContext(), TootActivity.class); Bundle b = new Bundle(); b.putString("sharedSubject", sharedSubject); @@ -599,6 +690,7 @@ public class MainActivity extends AppCompatActivity //Hide search bar on back pressed if( !toolbar_search.isIconified()){ toolbar_search.setIconified(true); + return; } if( viewPager.getVisibility() == View.VISIBLE){ if (stackBack.size() > 1) { @@ -615,7 +707,7 @@ public class MainActivity extends AppCompatActivity unCheckAllMenuItems(navigationView); toot.setVisibility(View.VISIBLE); //Manages theme for icon colors - SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, android.content.Context.MODE_PRIVATE); + SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK); if( theme == Helper.THEME_DARK){ changeDrawableColor(getApplicationContext(), R.drawable.ic_reply,R.color.dark_text); @@ -800,22 +892,25 @@ public class MainActivity extends AppCompatActivity @Override public void onResume(){ super.onResume(); - SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); - boolean bubbles = sharedpreferences.getBoolean(Helper.SET_BUBBLE_COUNTER, true); - if( bubbles){ - Handler handler = new Handler(); - handler.postDelayed(new Runnable() { - @Override - public void run() {refreshData();} - }, 1000); - } + MainActivity.activityResumed(); + updateNotifCounter(); + updateHomeCounter(); //Proceeds to update of the authenticated account if(Helper.isLoggedIn(getApplicationContext())) new UpdateAccountInfoByIDAsyncTask(getApplicationContext(), MainActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } + @Override + protected void onPause() { + super.onPause(); + MainActivity.activityPaused(); + } - + @Override + public void onDestroy(){ + super.onDestroy(); + LocalBroadcastManager.getInstance(this).unregisterReceiver(receive_data); + } @SuppressWarnings("StatementWithEmptyBody") @Override @@ -838,20 +933,25 @@ public class MainActivity extends AppCompatActivity } toolbarTitle.setText(item.getTitle()); if (id == R.id.nav_home) { - //noinspection ConstantConditions - tabLayout.getTabAt(0).select(); + if( tabLayout.getSelectedTabPosition() != 0) + //noinspection ConstantConditions + tabLayout.getTabAt(0).select(); return true; } else if( id == R.id.nav_notification){ - //noinspection ConstantConditions - tabLayout.getTabAt(1).select(); + if( tabLayout.getSelectedTabPosition() != 1) + //noinspection ConstantConditions + tabLayout.getTabAt(1).select(); return true; }else if (id == R.id.nav_local) { - //noinspection ConstantConditions - tabLayout.getTabAt(2).select(); + + if( tabLayout.getSelectedTabPosition() != 2) + //noinspection ConstantConditions + tabLayout.getTabAt(2).select(); return true; } else if (id == R.id.nav_global) { - //noinspection ConstantConditions - tabLayout.getTabAt(3).select(); + if( tabLayout.getSelectedTabPosition() != 3) + //noinspection ConstantConditions + tabLayout.getTabAt(3).select(); return true; } DisplayStatusFragment statusFragment; @@ -948,7 +1048,16 @@ public class MainActivity extends AppCompatActivity } - + @Override + public void onRetrieveMetaData(boolean error, String image, String title, String description) { + if( !error) { + Intent intentSendImage = new Intent(Helper.RECEIVE_PICTURE); + intentSendImage.putExtra("image", image); + intentSendImage.putExtra("title", title); + intentSendImage.putExtra("description", description); + LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intentSendImage); + } + } /** @@ -973,26 +1082,29 @@ public class MainActivity extends AppCompatActivity //Selection comes from another menu, no action to do DisplayStatusFragment statusFragment; Bundle bundle = new Bundle(); - switch (position) { - case 0: - homeFragment = new DisplayStatusFragment(); - bundle.putSerializable("type", RetrieveFeedsAsyncTask.Type.HOME); - homeFragment.setArguments(bundle); - return homeFragment; - case 1: - notificationsFragment = new DisplayNotificationsFragment(); - return notificationsFragment; - case 2: + if (position == 0) { + homeFragment = new DisplayStatusFragment(); + bundle.putSerializable("type", RetrieveFeedsAsyncTask.Type.HOME); + homeFragment.setArguments(bundle); + return homeFragment; + }else if( position == 1) { + notificationsFragment = new DisplayNotificationsFragment(); + return notificationsFragment; + }else if( position == 2 && display_local) { statusFragment = new DisplayStatusFragment(); bundle.putSerializable("type", RetrieveFeedsAsyncTask.Type.LOCAL); statusFragment.setArguments(bundle); return statusFragment; - case 3: + }else if( position == 2 && !display_local){ statusFragment = new DisplayStatusFragment(); bundle.putSerializable("type", RetrieveFeedsAsyncTask.Type.PUBLIC); statusFragment.setArguments(bundle); return statusFragment; - + }else if (position == 3){ + statusFragment = new DisplayStatusFragment(); + bundle.putSerializable("type", RetrieveFeedsAsyncTask.Type.PUBLIC); + statusFragment.setArguments(bundle); + return statusFragment; } return null; } @@ -1003,40 +1115,8 @@ public class MainActivity extends AppCompatActivity } } - private void refreshData(){ - final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, android.content.Context.MODE_PRIVATE); - String prefKeyOauthTokenT = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null); - SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); - Account account = new AccountDAO(getApplicationContext(), db).getAccountByToken(prefKeyOauthTokenT); - if( account != null){ - String last_refresh = sharedpreferences.getString(Helper.LAST_BUBBLE_REFRESH_NOTIF + account.getId(), null); - Date last_refresh_date = Helper.stringToDate(getApplicationContext(), last_refresh); - if (last_refresh_date == null || (new Date().getTime() - last_refresh_date.getTime()) >= TimeUnit.SECONDS.toMillis(60)) { - - if( notificationsFragment != null ){ - notificationsFragment.update(); - SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.LAST_BUBBLE_REFRESH_NOTIF+ account.getId(),Helper.dateToString(getApplicationContext(), new Date())); - editor.apply(); - } - } - - last_refresh = sharedpreferences.getString(Helper.LAST_BUBBLE_REFRESH_HOME + account.getId(), null); - last_refresh_date = Helper.stringToDate(getApplicationContext(), last_refresh); - - if (last_refresh_date == null || (new Date().getTime() - last_refresh_date.getTime()) >= TimeUnit.SECONDS.toMillis(60)) { - if( homeFragment != null ){ - homeFragment.update(); - SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.LAST_BUBBLE_REFRESH_HOME+ account.getId(),Helper.dateToString(getApplicationContext(), new Date())); - editor.apply(); - } - } - } - } - - public void updateHomeCounter(int newHomeCount){ + public void updateHomeCounter(){ if( tabLayout.getTabAt(0) == null ) return; //noinspection ConstantConditions @@ -1044,20 +1124,17 @@ public class MainActivity extends AppCompatActivity if( tabHome == null) return; TextView tabCounterHome = (TextView) tabHome.findViewById(R.id.tab_counter); - tabCounterHome.setText(String.valueOf(newHomeCount)); - if( newHomeCount > 0){ + tabCounterHome.setText(String.valueOf(Helper.getUnreadToots(getApplicationContext(), null))); + if( Helper.getUnreadToots(getApplicationContext(), null) > 0){ //New data are available //The fragment is not displayed, so the counter is displayed - if( tabLayout.getSelectedTabPosition() != 0) - tabCounterHome.setVisibility(View.VISIBLE); - else - tabCounterHome.setVisibility(View.GONE); + tabCounterHome.setVisibility(View.VISIBLE); }else { tabCounterHome.setVisibility(View.GONE); } } - public void updateNotifCounter(int newNotifCount){ + public void updateNotifCounter(){ if(tabLayout.getTabAt(1) == null) return; //noinspection ConstantConditions @@ -1065,15 +1142,25 @@ public class MainActivity extends AppCompatActivity if( tabNotif == null) return; TextView tabCounterNotif = (TextView) tabNotif.findViewById(R.id.tab_counter); - tabCounterNotif.setText(String.valueOf(newNotifCount)); - if( newNotifCount > 0){ - if( tabLayout.getSelectedTabPosition() != 1) - tabCounterNotif.setVisibility(View.VISIBLE); - else - tabCounterNotif.setVisibility(View.GONE); + tabCounterNotif.setText(String.valueOf(Helper.getUnreadNotifications(getApplicationContext(), null))); + if( Helper.getUnreadNotifications(getApplicationContext(), null) > 0){ + tabCounterNotif.setVisibility(View.VISIBLE); }else { tabCounterNotif.setVisibility(View.GONE); } } + public static boolean isActivityVisible() { + return activityVisible; + } + + private static void activityResumed() { + activityVisible = true; + } + + private static void activityPaused() { + activityVisible = false; + } + + private static boolean activityVisible; } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 968e2e13f..838a98a6c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -24,6 +24,7 @@ + + + + + + + @@ -70,7 +79,6 @@ + developers = new ArrayList<>(); + private List contributors = new ArrayList<>(); + private AccountSearchDevAdapter accountSearchWebAdapterDeveloper; + private AccountSearchDevAdapter accountSearchWebAdapterContributors; @SuppressWarnings("deprecation") @Override @@ -73,7 +80,8 @@ public class AboutActivity extends AppCompatActivity implements OnRetrieveSearcA about_version.setText(getResources().getString(R.string.about_vesrion, version)); } catch (PackageManager.NameNotFoundException ignored) {} - about_developer = (Button) findViewById(R.id.about_developer); + ExpandableHeightListView lv_developers = (ExpandableHeightListView) findViewById(R.id.lv_developers); + ExpandableHeightListView lv_contributors = (ExpandableHeightListView) findViewById(R.id.lv_contributors); Button about_code = (Button) findViewById(R.id.about_code); Button about_license = (Button) findViewById(R.id.about_license); Button about_thekinrar = (Button) findViewById(R.id.about_thekinrar); @@ -93,17 +101,7 @@ public class AboutActivity extends AppCompatActivity implements OnRetrieveSearcA startActivity(browserIntent); } }); - if(Helper.isLoggedIn(getApplicationContext())) { - about_developer.setEnabled(false); - new RetrieveDeveloperAccountsAsyncTask(getApplicationContext(),AboutActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - about_developer.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://mastodon.etalab.gouv.fr/@tschneider")); - startActivity(browserIntent); - } - }); + about_license.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -120,32 +118,21 @@ public class AboutActivity extends AppCompatActivity implements OnRetrieveSearcA } }); - TextView about_thanks = (TextView) findViewById(R.id.about_thanks_dev); - String currentText = about_thanks.getText().toString(); - SpannableString spanned_thanks = new SpannableString(currentText); - int startPosition = spanned_thanks.toString().indexOf("@PhotonQyv"); - int endPosition = startPosition + "@PhotonQyv".length(); - spanned_thanks.setSpan(new ClickableSpan() { - @Override - public void onClick(View textView) { - Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://mastodon.xyz/@PhotonQyv")); - startActivity(browserIntent); - } - @Override - public void updateDrawState(TextPaint ds) { - super.updateDrawState(ds); - } - }, startPosition, endPosition, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - about_thanks.setText(spanned_thanks, TextView.BufferType.SPANNABLE); - about_thanks.setMovementMethod(LinkMovementMethod.getInstance()); if( theme == Helper.THEME_LIGHT) { - about_developer.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.white)); about_code.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.white)); about_thekinrar.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.white)); about_translation.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.white)); about_license.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.white)); } + + lv_contributors.setExpanded(true); + lv_developers.setExpanded(true); + accountSearchWebAdapterContributors = new AccountSearchDevAdapter(AboutActivity.this, contributors); + lv_contributors.setAdapter(accountSearchWebAdapterContributors); + accountSearchWebAdapterDeveloper = new AccountSearchDevAdapter(AboutActivity.this, developers); + lv_developers.setAdapter(accountSearchWebAdapterDeveloper); + new RetrieveDeveloperAccountsAsyncTask(getApplicationContext(), AboutActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } @@ -160,22 +147,116 @@ public class AboutActivity extends AppCompatActivity implements OnRetrieveSearcA } } + @Override - public void onRetrieveSearchAccounts(APIResponse apiResponse) { - about_developer.setEnabled(true); - final List accounts = apiResponse.getAccounts(); - if( accounts != null && accounts.size() > 0 && accounts.get(0) != null) { - about_developer.setOnClickListener(null); - about_developer.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - Intent intent = new Intent(AboutActivity.this, ShowAccountActivity.class); - Bundle b = new Bundle(); - b.putString("accountId", accounts.get(0).getId()); - intent.putExtras(b); - startActivity(intent); - } - }); + public void onRetrieveRemoteAccount(boolean error, String name, String username, String instance_name, boolean locked, String avatar, String bio, String statusCount, String followingCount, String followersCount) { + if( error){ + return; + } + Account account = new Account(); + account.setInstance(instance_name); + account.setAcct(username + "@" + instance_name); + account.setAvatar(avatar); + account.setDisplay_name(username); + account.setStatuses_count_str(statusCount); + account.setFollowers_count_str(followersCount); + account.setFollowing_count_str(followingCount); + account.setUsername(name); + account.setLocked(locked); + account.setNote(bio); + account.setFollowing(false); + account.setRemote(true); + + if( username.equals("@tschneider")) { + developers.add(account); + accountSearchWebAdapterDeveloper.notifyDataSetChanged(); + }else { + contributors.add(account); + accountSearchWebAdapterContributors.notifyDataSetChanged(); + } + + } + + @Override + public void onRetrieveSearchDevelopersAccounts(ArrayList accounts) { + if( accounts == null || accounts.size() == 0) { + new RetrieveRemoteAccountsAsyncTask("tschneider", "mastodon.etalab.gouv.fr", AboutActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + new RetrieveRemoteAccountsAsyncTask("PhotonQyv", "mastodon.xyz", AboutActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + new RetrieveRemoteAccountsAsyncTask("angrytux", "social.tchncs.de", AboutActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + return; + } + boolean tschneider = false; + boolean PhotonQyv = false; + boolean angrytux = false; + + for(Account account: accounts){ + if( account.getUsername().equals("tschneider")){ + account.setFollowing(false); + account.setRemote(false); + developers.add(account); + accountSearchWebAdapterDeveloper.notifyDataSetChanged(); + tschneider = true; + new RetrieveRelationshipAsyncTask(getApplicationContext(), account.getId(),AboutActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + if( account.getUsername().equals("PhotonQyv")){ + account.setFollowing(false); + account.setRemote(false); + contributors.add(account); + accountSearchWebAdapterContributors.notifyDataSetChanged(); + PhotonQyv = true; + new RetrieveRelationshipAsyncTask(getApplicationContext(), account.getId(),AboutActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + if( account.getUsername().equals("angrytux")){ + account.setFollowing(false); + account.setRemote(false); + contributors.add(account); + accountSearchWebAdapterContributors.notifyDataSetChanged(); + angrytux = true; + new RetrieveRelationshipAsyncTask(getApplicationContext(), account.getId(),AboutActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } + if( !tschneider) + new RetrieveRemoteAccountsAsyncTask("tschneider", "mastodon.etalab.gouv.fr", AboutActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + if( !PhotonQyv) + new RetrieveRemoteAccountsAsyncTask("PhotonQyv", "mastodon.xyz", AboutActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + if( !angrytux) + new RetrieveRemoteAccountsAsyncTask("angrytux", "social.tchncs.de", AboutActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + @Override + public void onResume(){ + super.onResume(); + if( developers != null && developers.size() > 0){ + for(Account account: developers){ + new RetrieveRelationshipAsyncTask(getApplicationContext(), account.getId(),AboutActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } + if( contributors != null && contributors.size() > 0){ + for(Account account: contributors){ + new RetrieveRelationshipAsyncTask(getApplicationContext(), account.getId(),AboutActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + } + } + @Override + public void onRetrieveRelationship(Relationship relationship, Error error) { + SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, android.content.Context.MODE_PRIVATE); + String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, ""); + if( error != null){ + return; + } + for( int i = 0 ; i < developers.size() ; i++){ + if( contributors.get(i).getId() != null && developers.get(i).getId().equals(relationship.getId())){ + developers.get(i).setFollowing(relationship.isFollowing() || userId.trim().equals(relationship.getId())); + accountSearchWebAdapterDeveloper.notifyDataSetChanged(); + break; + } + } + for( int i = 0 ; i < contributors.size() ; i++){ + if( contributors.get(i).getId() != null && contributors.get(i).getId().equals(relationship.getId())){ + contributors.get(i).setFollowing(relationship.isFollowing() || userId.trim().equals(relationship.getId())); + accountSearchWebAdapterContributors.notifyDataSetChanged(); + break; + } } } } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/SettingsProfileFragment.java b/app/src/main/java/fr/gouv/etalab/mastodon/activities/EditProfileActivity.java similarity index 62% rename from app/src/main/java/fr/gouv/etalab/mastodon/fragments/SettingsProfileFragment.java rename to app/src/main/java/fr/gouv/etalab/mastodon/activities/EditProfileActivity.java index d7395099e..dd4aef975 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/SettingsProfileFragment.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/activities/EditProfileActivity.java @@ -1,4 +1,3 @@ -package fr.gouv.etalab.mastodon.fragments; /* Copyright 2017 Thomas Schneider * * This file is a part of Mastalab @@ -13,25 +12,32 @@ package fr.gouv.etalab.mastodon.fragments; * * You should have received a copy of the GNU General Public License along with Mastalab; if not, * see . */ +package fr.gouv.etalab.mastodon.activities; + + +import android.annotation.SuppressLint; import android.app.Activity; -import android.content.SharedPreferences; -import android.support.v4.content.ContextCompat; -import android.support.v7.app.AlertDialog; -import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.SharedPreferences; +import android.database.sqlite.SQLiteDatabase; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; -import android.support.v4.app.Fragment; +import android.support.v4.content.ContextCompat; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; import android.text.Editable; import android.text.Html; import android.text.TextWatcher; import android.util.Base64; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.Button; @@ -40,35 +46,43 @@ import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; +import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache; import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.ImageLoader; +import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; +import com.nostra13.universalimageloader.core.assist.FailReason; import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer; +import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.FileNotFoundException; import java.io.InputStream; - -import fr.gouv.etalab.mastodon.activities.MainActivity; import fr.gouv.etalab.mastodon.asynctasks.RetrieveAccountInfoAsyncTask; import fr.gouv.etalab.mastodon.asynctasks.UpdateCredentialAsyncTask; import fr.gouv.etalab.mastodon.client.APIResponse; import fr.gouv.etalab.mastodon.client.Entities.Account; import fr.gouv.etalab.mastodon.client.Entities.Error; +import fr.gouv.etalab.mastodon.client.PatchBaseImageDownloader; import fr.gouv.etalab.mastodon.helper.Helper; import fr.gouv.etalab.mastodon.interfaces.OnRetrieveAccountInterface; import fr.gouv.etalab.mastodon.interfaces.OnUpdateCredentialInterface; +import fr.gouv.etalab.mastodon.sqlite.AccountDAO; +import fr.gouv.etalab.mastodon.sqlite.Sqlite; import mastodon.etalab.gouv.fr.mastodon.R; + /** - * Created by Thomas on 04/06/2017. - * Fragment for profile settings + * Created by Thomas on 27/08/2017. + * Edit profile activity */ -public class SettingsProfileFragment extends Fragment implements OnRetrieveAccountInterface, OnUpdateCredentialInterface { + +public class EditProfileActivity extends AppCompatActivity implements OnRetrieveAccountInterface, OnUpdateCredentialInterface { + - private Context context; private EditText set_profile_name, set_profile_description; private ImageView set_profile_picture, set_header_picture; private Button set_change_profile_picture, set_change_header_picture, set_profile_save; @@ -79,59 +93,128 @@ public class SettingsProfileFragment extends Fragment implements OnRetrieveAccou private static final int PICK_IMAGE_PROFILE = 6545; private String profile_picture, header_picture, profile_username, profile_note; private Bitmap profile_picture_bmp, profile_header_bmp; + private TextView title; + private ImageView pp_actionBar; @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); - View rootView = inflater.inflate(R.layout.fragment_settings_profile, container, false); + SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, android.content.Context.MODE_PRIVATE); + int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK); + if( theme == Helper.THEME_LIGHT){ + setTheme(R.style.AppTheme); + }else { + setTheme(R.style.AppThemeDark); + } + setContentView(R.layout.activity_edit_profile); - set_profile_name = (EditText) rootView.findViewById(R.id.set_profile_name); - set_profile_description = (EditText) rootView.findViewById(R.id.set_profile_description); - set_profile_picture = (ImageView) rootView.findViewById(R.id.set_profile_picture); - set_header_picture = (ImageView) rootView.findViewById(R.id.set_header_picture); - set_change_profile_picture = (Button) rootView.findViewById(R.id.set_change_profile_picture); - set_change_header_picture = (Button) rootView.findViewById(R.id.set_change_header_picture); - set_profile_save = (Button) rootView.findViewById(R.id.set_profile_save); - set_header_picture_overlay = (TextView) rootView.findViewById(R.id.set_header_picture_overlay); + ActionBar actionBar = getSupportActionBar(); + if( actionBar != null) { + LayoutInflater inflater = (LayoutInflater) this.getSystemService(android.content.Context.LAYOUT_INFLATER_SERVICE); + View view = inflater.inflate(R.layout.conversation_action_bar, null); + actionBar.setCustomView(view, new ActionBar.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM); + title = (TextView) actionBar.getCustomView().findViewById(R.id.toolbar_title); + pp_actionBar = (ImageView) actionBar.getCustomView().findViewById(R.id.pp_actionBar); + title.setText(R.string.settings_title_profile); + ImageView close_conversation = (ImageView) actionBar.getCustomView().findViewById(R.id.close_conversation); + if( close_conversation != null){ + close_conversation.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + finish(); + } + }); + } + }else{ + setTitle(R.string.settings_title_profile); + } + SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); + Account account = new AccountDAO(getApplicationContext(),db).getAccountByID(userId); + String url = account.getAvatar(); + if( url.startsWith("/") ){ + url = "https://" + Helper.getLiveInstance(getApplicationContext()) + account.getAvatar(); + } + ImageLoader imageLoader = ImageLoader.getInstance(); + File cacheDir = new File(getCacheDir(), getString(R.string.app_name)); + ImageLoaderConfiguration configImg = new ImageLoaderConfiguration.Builder(this) + .imageDownloader(new PatchBaseImageDownloader(getApplicationContext())) + .threadPoolSize(5) + .threadPriority(Thread.MIN_PRIORITY + 3) + .denyCacheImageMultipleSizesInMemory() + .diskCache(new UnlimitedDiskCache(cacheDir)) + .build(); + + this.imageLoader = ImageLoader.getInstance(); + this.options = new DisplayImageOptions.Builder().displayer(new SimpleBitmapDisplayer()).cacheInMemory(false) + .cacheOnDisk(true).resetViewBeforeLoading(true).build(); + + imageLoader.init(configImg); + DisplayImageOptions options = new DisplayImageOptions.Builder().displayer(new SimpleBitmapDisplayer()).cacheInMemory(false) + .cacheOnDisk(true).resetViewBeforeLoading(true).build(); + imageLoader.loadImage(url, options, new SimpleImageLoadingListener(){ + @Override + public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { + super.onLoadingComplete(imageUri, view, loadedImage); + BitmapDrawable ppDrawable = new BitmapDrawable(getResources(), Bitmap.createScaledBitmap(loadedImage, (int) Helper.convertDpToPixel(25, getApplicationContext()), (int) Helper.convertDpToPixel(25, getApplicationContext()), true)); + if( pp_actionBar != null){ + pp_actionBar.setImageDrawable(ppDrawable); + } else if( getSupportActionBar() != null){ + + getSupportActionBar().setIcon(ppDrawable); + getSupportActionBar().setDisplayShowHomeEnabled(true); + } + } + @Override + public void onLoadingFailed(java.lang.String imageUri, android.view.View view, FailReason failReason){ + + }}); + + set_profile_name = (EditText) findViewById(R.id.set_profile_name); + set_profile_description = (EditText) findViewById(R.id.set_profile_description); + set_profile_picture = (ImageView) findViewById(R.id.set_profile_picture); + set_header_picture = (ImageView) findViewById(R.id.set_header_picture); + set_change_profile_picture = (Button) findViewById(R.id.set_change_profile_picture); + set_change_header_picture = (Button) findViewById(R.id.set_change_header_picture); + set_profile_save = (Button) findViewById(R.id.set_profile_save); + set_header_picture_overlay = (TextView) findViewById(R.id.set_header_picture_overlay); set_profile_save.setEnabled(false); set_change_header_picture.setEnabled(false); set_change_profile_picture.setEnabled(false); set_profile_name.setEnabled(false); set_profile_description.setEnabled(false); - context = getContext(); - imageLoader = ImageLoader.getInstance(); - options = new DisplayImageOptions.Builder().displayer(new SimpleBitmapDisplayer()).cacheInMemory(false) - .cacheOnDisk(true).resetViewBeforeLoading(true).build(); - new RetrieveAccountInfoAsyncTask(context, SettingsProfileFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, android.content.Context.MODE_PRIVATE); - final int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK); + + new RetrieveAccountInfoAsyncTask(getApplicationContext(), EditProfileActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); if( theme == Helper.THEME_LIGHT) { - set_profile_save.setTextColor(ContextCompat.getColor(context, R.color.white)); + set_profile_save.setTextColor(ContextCompat.getColor(getApplicationContext(), R.color.white)); } - return rootView; } - - @Override - public void onCreate(Bundle saveInstance) { - super.onCreate(saveInstance); + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + finish(); + return true; + default: + return super.onOptionsItemSelected(item); + } } - - @Override - public void onAttach(Context context) { - super.onAttach(context); - this.context = context; + public boolean onCreateOptionsMenu(Menu menu) { + // Inflate the menu; this adds items to the action bar if it is present. + getMenuInflater().inflate(R.menu.main_media, menu); + return true; } - @Override public void onRetrieveAccount(Account account, Error error) { if( error != null ){ - Toast.makeText(context,R.string.toast_error, Toast.LENGTH_LONG).show(); + Toast.makeText(getApplicationContext(),R.string.toast_error, Toast.LENGTH_LONG).show(); return; } set_profile_name.setText(account.getDisplay_name()); @@ -161,7 +244,7 @@ public class SettingsProfileFragment extends Fragment implements OnRetrieveAccou String content = s.toString().substring(0,160); set_profile_description.setText(content); set_profile_description.setSelection(set_profile_description.getText().length()); - Toast.makeText(context,R.string.note_no_space,Toast.LENGTH_LONG).show(); + Toast.makeText(getApplicationContext(),R.string.note_no_space,Toast.LENGTH_LONG).show(); } } }); @@ -177,7 +260,7 @@ public class SettingsProfileFragment extends Fragment implements OnRetrieveAccou String content = s.toString().substring(0,30); set_profile_name.setText(content); set_profile_name.setSelection(set_profile_name.getText().length()); - Toast.makeText(context,R.string.username_no_space,Toast.LENGTH_LONG).show(); + Toast.makeText(getApplicationContext(),R.string.username_no_space,Toast.LENGTH_LONG).show(); } } }); @@ -233,9 +316,9 @@ public class SettingsProfileFragment extends Fragment implements OnRetrieveAccou else profile_note = null; - AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context); - LayoutInflater inflater = ((MainActivity) context).getLayoutInflater(); - View dialogView = inflater.inflate(R.layout.dialog_profile, null); + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(EditProfileActivity.this); + LayoutInflater inflater = EditProfileActivity.this.getLayoutInflater(); + @SuppressLint("InflateParams") View dialogView = inflater.inflate(R.layout.dialog_profile, null); dialogBuilder.setView(dialogView); ImageView back_ground_image = (ImageView) dialogView.findViewById(R.id.back_ground_image); @@ -248,7 +331,7 @@ public class SettingsProfileFragment extends Fragment implements OnRetrieveAccou if( profile_note != null) dialog_profile_description.setText(profile_note); if( profile_header_bmp != null) { - BitmapDrawable background = new BitmapDrawable(context.getResources(), profile_header_bmp); + BitmapDrawable background = new BitmapDrawable(getApplicationContext().getResources(), profile_header_bmp); if(Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { //noinspection deprecation back_ground_image.setBackgroundDrawable(background); @@ -264,7 +347,7 @@ public class SettingsProfileFragment extends Fragment implements OnRetrieveAccou } } if( profile_picture_bmp != null) { - BitmapDrawable background = new BitmapDrawable(context.getResources(), profile_picture_bmp); + BitmapDrawable background = new BitmapDrawable(getApplicationContext().getResources(), profile_picture_bmp); if(Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { //noinspection deprecation dialog_profile_picture.setBackgroundDrawable(background); @@ -283,7 +366,7 @@ public class SettingsProfileFragment extends Fragment implements OnRetrieveAccou @Override public void onClick(DialogInterface dialog, int id) { set_profile_save.setEnabled(false); - new UpdateCredentialAsyncTask(context, profile_username, profile_note, profile_picture, header_picture, SettingsProfileFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + new UpdateCredentialAsyncTask(getApplicationContext(), profile_username, profile_note, profile_picture, header_picture, EditProfileActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } }); dialogBuilder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { @@ -305,16 +388,16 @@ public class SettingsProfileFragment extends Fragment implements OnRetrieveAccou super.onActivityResult(requestCode, resultCode, data); if (requestCode == PICK_IMAGE_HEADER && resultCode == Activity.RESULT_OK) { if (data == null) { - Toast.makeText(context,R.string.toot_select_image_error,Toast.LENGTH_LONG).show(); + Toast.makeText(getApplicationContext(),R.string.toot_select_image_error,Toast.LENGTH_LONG).show(); return; } try { - InputStream inputStream = context.getContentResolver().openInputStream(data.getData()); + InputStream inputStream = getApplicationContext().getContentResolver().openInputStream(data.getData()); BufferedInputStream bufferedInputStream; if (inputStream != null) { bufferedInputStream = new BufferedInputStream(inputStream); }else { - Toast.makeText(context,R.string.toot_select_image_error,Toast.LENGTH_LONG).show(); + Toast.makeText(getApplicationContext(),R.string.toot_select_image_error,Toast.LENGTH_LONG).show(); return; } Bitmap bmp = BitmapFactory.decodeStream(bufferedInputStream); @@ -326,21 +409,21 @@ public class SettingsProfileFragment extends Fragment implements OnRetrieveAccou header_picture = "data:image/png;base64, " + Base64.encodeToString(byteArray, Base64.DEFAULT); } catch (FileNotFoundException e) { - Toast.makeText(context,R.string.toot_select_image_error,Toast.LENGTH_LONG).show(); + Toast.makeText(getApplicationContext(),R.string.toot_select_image_error,Toast.LENGTH_LONG).show(); e.printStackTrace(); } }else if(requestCode == PICK_IMAGE_PROFILE && resultCode == Activity.RESULT_OK) { if (data == null) { - Toast.makeText(context,R.string.toot_select_image_error,Toast.LENGTH_LONG).show(); + Toast.makeText(getApplicationContext(),R.string.toot_select_image_error,Toast.LENGTH_LONG).show(); return; } try { - InputStream inputStream = context.getContentResolver().openInputStream(data.getData()); + InputStream inputStream = getApplicationContext().getContentResolver().openInputStream(data.getData()); BufferedInputStream bufferedInputStream; if (inputStream != null) { bufferedInputStream = new BufferedInputStream(inputStream); }else { - Toast.makeText(context,R.string.toot_select_image_error,Toast.LENGTH_LONG).show(); + Toast.makeText(getApplicationContext(),R.string.toot_select_image_error,Toast.LENGTH_LONG).show(); return; } Bitmap bmp = BitmapFactory.decodeStream(bufferedInputStream); @@ -351,7 +434,7 @@ public class SettingsProfileFragment extends Fragment implements OnRetrieveAccou byte[] byteArray = byteArrayOutputStream .toByteArray(); profile_picture = "data:image/png;base64, " + Base64.encodeToString(byteArray, Base64.DEFAULT); } catch (FileNotFoundException e) { - Toast.makeText(context,R.string.toot_select_image_error,Toast.LENGTH_LONG).show(); + Toast.makeText(getApplicationContext(),R.string.toot_select_image_error,Toast.LENGTH_LONG).show(); e.printStackTrace(); } } @@ -360,10 +443,11 @@ public class SettingsProfileFragment extends Fragment implements OnRetrieveAccou @Override public void onUpdateCredential(APIResponse apiResponse) { if( apiResponse.getError() != null){ - Toast.makeText(context, R.string.toast_error, Toast.LENGTH_LONG).show(); + Toast.makeText(getApplicationContext(), R.string.toast_error, Toast.LENGTH_LONG).show(); return; } - Toast.makeText(context, R.string.toast_update_credential_ok, Toast.LENGTH_LONG).show(); + Toast.makeText(getApplicationContext(), R.string.toast_update_credential_ok, Toast.LENGTH_LONG).show(); set_profile_save.setEnabled(true); } + } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/activities/MediaActivity.java b/app/src/main/java/fr/gouv/etalab/mastodon/activities/MediaActivity.java index 2a4e882cf..f1dced4a9 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/activities/MediaActivity.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/activities/MediaActivity.java @@ -362,6 +362,8 @@ public class MediaActivity extends AppCompatActivity { @Override public void onPrepared(MediaPlayer mp) { loader.setVisibility(View.GONE); + mp.start(); + mp.setLooping(true); } }); videoView.setVisibility(View.VISIBLE); diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/activities/RemoteFollowActivity.java b/app/src/main/java/fr/gouv/etalab/mastodon/activities/RemoteFollowActivity.java index 55262e673..5e626391c 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/activities/RemoteFollowActivity.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/activities/RemoteFollowActivity.java @@ -261,7 +261,7 @@ public class RemoteFollowActivity extends AppCompatActivity implements OnRetriev @Override - public void onRetrieveRemoteAccount(boolean error, String name, String username, boolean locked, String avatar, String bio, int statusCount, int followingCount, int followersCount) { + public void onRetrieveRemoteAccount(boolean error, String name, String username, String instance_name, boolean locked, String avatar, String bio, String statusCount, String followingCount, String followersCount) { loader.setVisibility(View.GONE); rf_search.setEnabled(true); if( error){ @@ -274,9 +274,9 @@ public class RemoteFollowActivity extends AppCompatActivity implements OnRetriev account.setAcct(screen_name + "@" + instance_name); account.setAvatar(avatar); account.setDisplay_name(username); - account.setStatuses_count(statusCount); - account.setFollowers_count(followersCount); - account.setFollowing_count(followingCount); + account.setStatuses_count_str(statusCount); + account.setFollowers_count_str(followersCount); + account.setFollowing_count_str(followingCount); account.setUsername(name); account.setLocked(locked); account.setNote(bio); diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/activities/ShowAccountActivity.java b/app/src/main/java/fr/gouv/etalab/mastodon/activities/ShowAccountActivity.java index e25e763c5..577cbcff8 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/activities/ShowAccountActivity.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/activities/ShowAccountActivity.java @@ -20,6 +20,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; +import android.database.sqlite.SQLiteDatabase; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; @@ -89,6 +90,8 @@ import fr.gouv.etalab.mastodon.interfaces.OnPostActionInterface; import fr.gouv.etalab.mastodon.interfaces.OnRetrieveAccountInterface; import fr.gouv.etalab.mastodon.interfaces.OnRetrieveFeedsAccountInterface; import fr.gouv.etalab.mastodon.interfaces.OnRetrieveRelationshipInterface; +import fr.gouv.etalab.mastodon.sqlite.AccountDAO; +import fr.gouv.etalab.mastodon.sqlite.Sqlite; import mastodon.etalab.gouv.fr.mastodon.R; import fr.gouv.etalab.mastodon.client.Entities.Relationship; @@ -122,6 +125,7 @@ public class ShowAccountActivity extends AppCompatActivity implements OnPostActi private BroadcastReceiver hide_header; private boolean isHiddingShowing = false; private LinearLayout main_header_container; + private ImageView header_edit_profile; public enum action{ FOLLOW, @@ -149,6 +153,7 @@ public class ShowAccountActivity extends AppCompatActivity implements OnPostActi account_follow = (FloatingActionButton) findViewById(R.id.account_follow); account_follow_request = (TextView) findViewById(R.id.account_follow_request); main_header_container = (LinearLayout) findViewById(R.id.main_header_container); + header_edit_profile = (ImageView) findViewById(R.id.header_edit_profile); account_follow.setEnabled(false); if(b != null){ accountId = b.getString("accountId"); @@ -298,6 +303,14 @@ public class ShowAccountActivity extends AppCompatActivity implements OnPostActi LocalBroadcastManager.getInstance(this).registerReceiver(hide_header, new IntentFilter(Helper.HEADER_ACCOUNT + String.valueOf(instanceValue))); } + + header_edit_profile.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(ShowAccountActivity.this, EditProfileActivity.class); + startActivity(intent); + } + }); } @@ -520,6 +533,7 @@ public class ShowAccountActivity extends AppCompatActivity implements OnPostActi account_follow.setEnabled(true); if( accountId != null && accountId.equals(userId)){ account_follow.setVisibility(View.GONE); + header_edit_profile.setVisibility(View.VISIBLE); }else if( relationship.isBlocking()){ account_follow.setImageResource(R.drawable.ic_unlock_alt); doAction = action.UNBLOCK; diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/activities/TootActivity.java b/app/src/main/java/fr/gouv/etalab/mastodon/activities/TootActivity.java index f069213f4..c56c7ceee 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/activities/TootActivity.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/activities/TootActivity.java @@ -17,11 +17,14 @@ package fr.gouv.etalab.mastodon.activities; import android.Manifest; import android.annotation.SuppressLint; import android.app.Activity; +import android.content.BroadcastReceiver; +import android.content.IntentFilter; import android.content.pm.PackageManager; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.net.Uri; +import android.support.v4.content.LocalBroadcastManager; import android.support.v7.app.AlertDialog; import android.content.ActivityNotFoundException; import android.content.Context; @@ -49,6 +52,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.view.inputmethod.InputMethodManager; +import android.webkit.URLUtil; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.AutoCompleteTextView; @@ -66,6 +70,8 @@ import android.widget.TextView; import android.widget.TimePicker; import android.widget.Toast; +import com.loopj.android.http.AsyncHttpClient; +import com.loopj.android.http.BinaryHttpResponseHandler; import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache; import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.ImageLoader; @@ -74,9 +80,13 @@ import com.nostra13.universalimageloader.core.assist.FailReason; import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer; import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; @@ -86,6 +96,7 @@ import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; +import cz.msebera.android.httpclient.Header; import fr.gouv.etalab.mastodon.asynctasks.PostStatusAsyncTask; import fr.gouv.etalab.mastodon.asynctasks.RetrieveSearchAccountsAsyncTask; import fr.gouv.etalab.mastodon.asynctasks.UploadActionAsyncTask; @@ -136,7 +147,7 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc private EditText toot_cw_content; private LinearLayout toot_reply_content_container; private Status tootReply = null; - private String sharedContent, sharedSubject; + private String sharedContent, sharedSubject, sharedContentIni; private CheckBox toot_sensitive; public long currentToId; private long restored; @@ -148,7 +159,9 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc private HorizontalScrollView picture_scrollview; private int currentCursorPosition, searchLength; private TextView toot_space_left; + private String initialContent; private final int MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 754; + private BroadcastReceiver receive_picture; @Override protected void onCreate(Bundle savedInstanceState) { @@ -240,6 +253,7 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc if(b != null) { tootReply = b.getParcelable("tootReply"); sharedContent = b.getString("sharedContent", null); + sharedContentIni = b.getString("sharedContent", null); sharedSubject = b.getString("sharedSubject", null); // ACTION_SEND route if (b.getInt("uriNumber", 0) == 1) { @@ -261,6 +275,7 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc } restored = b.getLong("restored", -1); } + initialContent = toot_content.getText().toString(); if( restored != -1 ){ toot_it.setVisibility(View.GONE); invalidateOptionsMenu(); @@ -299,21 +314,75 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc }}); if( sharedContent != null ){ //Shared content + if( sharedSubject != null){ sharedContent = sharedSubject + "\n\n" + sharedContent; } + receive_picture = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final String image = intent.getStringExtra("image"); + String title = intent.getStringExtra("title"); + String description = intent.getStringExtra("description"); + if( description != null && description.length() > 20 ){ + if( description.length() > 200 ) + description = description.substring(0,199) + "…"; + if( title != null) + sharedContent = title + "\n\n" + description + "\n\n" + sharedContentIni; + else + sharedContent = description + "\n\n" + sharedContentIni; + toot_content.setText(sharedContent); + toot_space_left.setText(String.valueOf(toot_content.length())); + toot_content.setSelection(toot_content.getText().length()); + } + if( image != null){ + AsyncHttpClient client = new AsyncHttpClient(); + String[] allowedTypes = new String[] { "image/png","image/jpeg" }; + client.get(image, new BinaryHttpResponseHandler(allowedTypes) { + @Override + public void onSuccess(int statusCode, Header[] headers, byte[] binaryData) { + OutputStream f; + try { + f = new FileOutputStream(getCacheDir() + URLUtil.guessFileName(image, null, null)); + picture_scrollview.setVisibility(View.VISIBLE); + InputStream bis = new ByteArrayInputStream(binaryData); + loading_picture.setVisibility(View.VISIBLE); + toot_picture.setEnabled(false); + new UploadActionAsyncTask(getApplicationContext(),bis,TootActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + f.write(binaryData); + f.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public void onFailure(int statusCode, Header[] headers, byte[] binaryData, Throwable error) { + error.printStackTrace(); + } + + }); + } + + } + }; + LocalBroadcastManager.getInstance(this).registerReceiver(receive_picture, new IntentFilter(Helper.RECEIVE_PICTURE)); toot_content.setText( String.format("\n%s", sharedContent)); + toot_space_left.setText(String.valueOf(toot_content.length())); } attachments = new ArrayList<>(); int charsInCw = 0; int charsInToot = 0; - uploadSharedImage(sharedUri); + if (!sharedUri.isEmpty()) { + uploadSharedImage(sharedUri); + } boolean isAccountPrivate = account.isLocked(); if(isAccountPrivate){ if( tootReply == null) { visibility = "private"; + toot_visibility.setImageResource(R.drawable.ic_action_lock_closed); }else { if( visibility.equals("direct") ){ toot_visibility.setImageResource(R.drawable.ic_local_post_office); @@ -523,6 +592,14 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc } } + @Override + public void onDestroy(){ + super.onDestroy(); + if( receive_picture != null) + LocalBroadcastManager.getInstance(this).unregisterReceiver(receive_picture); + } + + @Override public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { @@ -546,6 +623,7 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc mToast.show(); } + // Handles uploading shared images public void uploadSharedImage(ArrayList uri) { @@ -769,6 +847,7 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc //Clear content toot_content.setText(""); toot_cw_content.setText(""); + toot_space_left.setText(0); if( attachments != null) { for (Attachment attachment : attachments) { View namebar = findViewById(Integer.parseInt(attachment.getId())); @@ -842,6 +921,7 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc //Adds the shorter text_url of attachment at the end of the toot
 int selectionBefore = toot_content.getSelectionStart(); toot_content.setText(toot_content.getText().toString() + "\n" + attachment.getText_url()); + toot_space_left.setText(String.valueOf(toot_content.length())); //Moves the cursor if (selectionBefore >= 0) toot_content.setSelection(selectionBefore); @@ -890,6 +970,7 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc //Clears the text_url at the end of the toot
 for this attachment int selectionBefore = toot_content.getSelectionStart(); toot_content.setText(toot_content.getText().toString().replace(attachment.getText_url(), "")); + toot_space_left.setText(String.valueOf(toot_content.length())); //Moves the cursor if (selectionBefore >= 0) toot_content.setSelection(selectionBefore); @@ -977,6 +1058,7 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc //Clear the toot toot_content.setText(""); toot_cw_content.setText(""); + toot_space_left.setText("0"); if( attachments != null) { for (Attachment attachment : attachments) { View namebar = findViewById(Integer.parseInt(attachment.getId())); @@ -1059,6 +1141,7 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc if( currentCursorPosition < oldContent.length() - 1) newContent += oldContent.substring(currentCursorPosition, oldContent.length()-1); toot_content.setText(newContent); + toot_space_left.setText(String.valueOf(toot_content.length())); toot_content.setSelection(newPosition); AccountsSearchAdapter accountsListAdapter = new AccountsSearchAdapter(TootActivity.this, new ArrayList()); toot_content.setThreshold(1); @@ -1132,6 +1215,7 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc } String content = status.getContent(); toot_content.setText(content); + toot_space_left.setText(String.valueOf(toot_content.length())); toot_content.setSelection(toot_content.getText().length()); switch (status.getVisibility()){ case "public": @@ -1163,6 +1247,7 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc else setTitle(R.string.toot_title); } + initialContent = toot_content.getText().toString(); toot_space_left.setText(String.valueOf(toot_content.getText().length() + toot_cw_content.getText().length())); } @@ -1245,9 +1330,10 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc //Put a "dot" at the end of all mentioned account to force capitalization toot_content.append(" . "); } - + toot_space_left.setText(String.valueOf(toot_content.length())); toot_content.setSelection(toot_content.getText().length()); //Put cursor at the end } + initialContent = toot_content.getText().toString(); } @@ -1256,7 +1342,8 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc //Nothing to store here.... if(toot_content.getText().toString().trim().length() == 0 && (attachments == null || attachments.size() <1) && toot_cw_content.getText().toString().trim().length() == 0) return; - + if( initialContent.equals(toot_content.getText().toString())) + return; Status toot = new Status(); toot.setSensitive(isSensitive); toot.setMedia_attachments(attachments); @@ -1311,6 +1398,4 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc changeDrawableColor(TootActivity.this, R.drawable.ic_check, R.color.white); } } - - } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/activities/WebviewActivity.java b/app/src/main/java/fr/gouv/etalab/mastodon/activities/WebviewActivity.java index dc3b5d287..985ebb20b 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/activities/WebviewActivity.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/activities/WebviewActivity.java @@ -147,6 +147,20 @@ public class WebviewActivity extends AppCompatActivity { this.url = newUrl; } + @Override + public void onPause(){ + super.onPause(); + if( webView != null) + webView.onPause(); + } + + @Override + public void onResume(){ + super.onResume(); + if( webView != null) + webView.onResume(); + } + @Override public void onBackPressed() { if (webView.canGoBack()){ @@ -159,6 +173,7 @@ public class WebviewActivity extends AppCompatActivity { @Override public void onDestroy(){ super.onDestroy(); - + if( webView != null) + webView.destroy(); } } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveDeveloperAccountsAsyncTask.java b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveDeveloperAccountsAsyncTask.java index caa29d57a..63afa5432 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveDeveloperAccountsAsyncTask.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveDeveloperAccountsAsyncTask.java @@ -16,9 +16,13 @@ package fr.gouv.etalab.mastodon.asynctasks; import android.content.Context; import android.os.AsyncTask; + +import java.util.ArrayList; + import fr.gouv.etalab.mastodon.client.API; import fr.gouv.etalab.mastodon.client.APIResponse; -import fr.gouv.etalab.mastodon.interfaces.OnRetrieveSearcAccountshInterface; +import fr.gouv.etalab.mastodon.client.Entities.Account; +import fr.gouv.etalab.mastodon.interfaces.OnRetrieveSearchDevelopersAccountshInterface; /** @@ -29,23 +33,32 @@ import fr.gouv.etalab.mastodon.interfaces.OnRetrieveSearcAccountshInterface; public class RetrieveDeveloperAccountsAsyncTask extends AsyncTask { private Context context; - private APIResponse apiResponse; - private OnRetrieveSearcAccountshInterface listener; + private OnRetrieveSearchDevelopersAccountshInterface listener; + private ArrayList accounts; - public RetrieveDeveloperAccountsAsyncTask(Context context, OnRetrieveSearcAccountshInterface onRetrieveSearcAccountshInterface){ + public RetrieveDeveloperAccountsAsyncTask(Context context, OnRetrieveSearchDevelopersAccountshInterface onRetrieveSearchDevelopersAccountshInterface){ this.context = context; - this.listener = onRetrieveSearcAccountshInterface; + this.listener = onRetrieveSearchDevelopersAccountshInterface; } @Override protected Void doInBackground(Void... params) { API api = new API(context); - apiResponse = api.searchDeveloper(); + accounts = new ArrayList<>(); + APIResponse apiResponse = api.searchAccounts("@tschneider@mastodon.etalab.gouv.fr", 1); + if( apiResponse.getAccounts() != null && apiResponse.getAccounts().size() > 0) + accounts.add(apiResponse.getAccounts().get(0)); + apiResponse = api.searchAccounts("@PhotonQyv@mastodon.xyz",1); + if( apiResponse.getAccounts() != null && apiResponse.getAccounts().size() > 0) + accounts.add(apiResponse.getAccounts().get(0)); + apiResponse = api.searchAccounts("@angrytux@social.tchncs.de",1); + if( apiResponse.getAccounts() != null && apiResponse.getAccounts().size() > 0) + accounts.add(apiResponse.getAccounts().get(0)); return null; } @Override protected void onPostExecute(Void result) { - listener.onRetrieveSearchAccounts(apiResponse); + listener.onRetrieveSearchDevelopersAccounts(accounts); } } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveFeedsAsyncTask.java b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveFeedsAsyncTask.java index 4c913630e..99d93fd67 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveFeedsAsyncTask.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveFeedsAsyncTask.java @@ -15,10 +15,8 @@ package fr.gouv.etalab.mastodon.asynctasks; import android.content.Context; -import android.content.SharedPreferences; import android.os.AsyncTask; - -import java.util.Date; +import android.util.Log; import fr.gouv.etalab.mastodon.client.API; import fr.gouv.etalab.mastodon.client.APIResponse; @@ -61,8 +59,6 @@ public class RetrieveFeedsAsyncTask extends AsyncTask { this.max_id = max_id; this.listener = onRetrieveFeedsInterface; this.refreshData = true; - updateTimeRefresh(); - } public RetrieveFeedsAsyncTask(Context context, Type action, String targetedID, String max_id, boolean showMediaOnly, OnRetrieveFeedsInterface onRetrieveFeedsInterface){ @@ -73,7 +69,6 @@ public class RetrieveFeedsAsyncTask extends AsyncTask { this.targetedID = targetedID; this.showMediaOnly = showMediaOnly; this.refreshData = true; - updateTimeRefresh(); } public RetrieveFeedsAsyncTask(Context context, Type action, String tag, String targetedID, String max_id, OnRetrieveFeedsInterface onRetrieveFeedsInterface){ this.context = context; @@ -83,21 +78,11 @@ public class RetrieveFeedsAsyncTask extends AsyncTask { this.targetedID = targetedID; this.tag = tag; this.refreshData = true; - updateTimeRefresh(); } - public RetrieveFeedsAsyncTask(Context context, Type action, String max_id, boolean refreshData, OnRetrieveFeedsInterface onRetrieveFeedsInterface){ - this.context = context; - this.action = action; - this.max_id = max_id; - this.listener = onRetrieveFeedsInterface; - this.refreshData = refreshData; - updateTimeRefresh(); - } @Override protected Void doInBackground(Void... params) { - API api = new API(context); switch (action){ case HOME: @@ -134,14 +119,4 @@ public class RetrieveFeedsAsyncTask extends AsyncTask { protected void onPostExecute(Void result) { listener.onRetrieveFeeds(apiResponse, refreshData); } - - private void updateTimeRefresh(){ - if( action == Type.HOME) { - final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, android.content.Context.MODE_PRIVATE); - SharedPreferences.Editor editor = sharedpreferences.edit(); - String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); - editor.putString(Helper.LAST_BUBBLE_REFRESH_HOME + userId, Helper.dateToString(context, new Date())); - editor.apply(); - } - } } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveMetaDataAsyncTask.java b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveMetaDataAsyncTask.java new file mode 100644 index 000000000..788466cfc --- /dev/null +++ b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveMetaDataAsyncTask.java @@ -0,0 +1,75 @@ +/* Copyright 2017 Thomas Schneider + * + * This file is a part of Mastalab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Mastalab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Mastalab; if not, + * see . */ +package fr.gouv.etalab.mastodon.asynctasks; + +import android.os.AsyncTask; + +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; +import org.jsoup.select.Elements; +import java.io.IOException; +import fr.gouv.etalab.mastodon.interfaces.OnRetrieveMetaDataInterface; + + +/** + * Created by Thomas on 02/09/2017. + * Retrieves metadata of a remote page + */ + +public class RetrieveMetaDataAsyncTask extends AsyncTask { + + private OnRetrieveMetaDataInterface listener; + private String url; + private boolean error = false; + private String image, title, description; + + public RetrieveMetaDataAsyncTask(String url, OnRetrieveMetaDataInterface onRetrieveRemoteAccountInterface){ + this.url = url; + this.listener = onRetrieveRemoteAccountInterface; + } + + @Override + protected Void doInBackground(Void... params) { + String userAgent = "Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1667.0 Safari/537.36"; + try { + Document document = Jsoup.connect(url).userAgent(userAgent).get(); + Elements metaOgTitle = document.select("meta[property=og:title]"); + if (metaOgTitle != null) { + title = metaOgTitle.attr("content"); + } else { + title = document.title(); + } + Elements metaOgDescription = document.select("meta[property=og:description]"); + if (metaOgDescription != null) { + description = metaOgDescription.attr("content"); + } else { + description = ""; + } + Elements metaOgImage = document.select("meta[property=og:image]"); + if (metaOgImage != null) { + image = metaOgImage.attr("content"); + } + } catch (IOException | IndexOutOfBoundsException e) { + error = true; + } + return null; + } + + @Override + protected void onPostExecute(Void result) { + listener.onRetrieveMetaData(error, image, title, description); + } + +} diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveNotificationsAsyncTask.java b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveNotificationsAsyncTask.java index e59bb8417..6b7a8b7c6 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveNotificationsAsyncTask.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveNotificationsAsyncTask.java @@ -15,14 +15,9 @@ package fr.gouv.etalab.mastodon.asynctasks; import android.content.Context; -import android.content.SharedPreferences; import android.os.AsyncTask; - -import java.util.Date; - import fr.gouv.etalab.mastodon.client.API; import fr.gouv.etalab.mastodon.client.APIResponse; -import fr.gouv.etalab.mastodon.helper.Helper; import fr.gouv.etalab.mastodon.interfaces.OnRetrieveNotificationsInterface; @@ -51,20 +46,8 @@ public class RetrieveNotificationsAsyncTask extends AsyncTask this.userId = userId; this.token = token; this.refreshData = true; - updateTimeRefresh(); } - public RetrieveNotificationsAsyncTask(Context context, String instance, String token, String max_id, String acct, String userId, boolean refreshData, OnRetrieveNotificationsInterface onRetrieveNotificationsInterface){ - this.context = context; - this.max_id = max_id; - this.listener = onRetrieveNotificationsInterface; - this.acct = acct; - this.instance = instance; - this.userId = userId; - this.token = token; - this.refreshData = refreshData; - updateTimeRefresh(); - } @Override protected Void doInBackground(Void... params) { @@ -82,10 +65,4 @@ public class RetrieveNotificationsAsyncTask extends AsyncTask listener.onRetrieveNotifications(apiResponse, acct, userId, refreshData); } - private void updateTimeRefresh(){ - final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, android.content.Context.MODE_PRIVATE); - SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.LAST_BUBBLE_REFRESH_NOTIF+ userId,Helper.dateToString(context, new Date())); - editor.apply(); - } } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveRelationshipAsyncTask.java b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveRelationshipAsyncTask.java index 8347f0df4..84c62d9ba 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveRelationshipAsyncTask.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveRelationshipAsyncTask.java @@ -16,9 +16,11 @@ package fr.gouv.etalab.mastodon.asynctasks; import android.content.Context; import android.os.AsyncTask; +import android.util.Log; import fr.gouv.etalab.mastodon.client.API; import fr.gouv.etalab.mastodon.client.Entities.Relationship; +import fr.gouv.etalab.mastodon.helper.Helper; import fr.gouv.etalab.mastodon.interfaces.OnRetrieveRelationshipInterface; /** @@ -42,7 +44,6 @@ public class RetrieveRelationshipAsyncTask extends AsyncTask { @Override protected Void doInBackground(Void... params) { - api = new API(context); relationship = api.getRelationship(accountId); return null; diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveRemoteAccountsAsyncTask.java b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveRemoteAccountsAsyncTask.java index 94141cbd1..f8b2ef714 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveRemoteAccountsAsyncTask.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveRemoteAccountsAsyncTask.java @@ -15,10 +15,11 @@ package fr.gouv.etalab.mastodon.asynctasks; import android.os.AsyncTask; + import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.select.Elements; -import java.io.IOException; + import fr.gouv.etalab.mastodon.interfaces.OnRetrieveRemoteAccountInterface; @@ -32,7 +33,7 @@ public class RetrieveRemoteAccountsAsyncTask extends AsyncTask private OnRetrieveRemoteAccountInterface listener; private String url; private String avatar, name, username, bio; - private int statusCount, followingCount, followersCount; + private String statusCount, followingCount, followersCount; private boolean islocked; private boolean error = false; private String instance; @@ -56,23 +57,24 @@ public class RetrieveRemoteAccountsAsyncTask extends AsyncTask Elements nameElement = document.getElementsByClass("name"); name = nameElement.get(0).getElementsByClass("p-name").get(0).html(); username = nameElement.get(0).getElementsByTag("span").get(1).html(); - islocked = nameElement.get(0).getElementsByClass("fa-lock") != null; + islocked = (nameElement.get(0).getElementsByClass("fa-lock") != null && nameElement.get(0).getElementsByClass("fa-lock").size() > 0); Elements bioElement = document.getElementsByClass("bio"); bio = bioElement.get(0).html(); Elements countElement = document.getElementsByClass("counter-number"); - statusCount = Integer.parseInt(countElement.get(0).html()); - followingCount = Integer.parseInt(countElement.get(1).html()); - followersCount = Integer.parseInt(countElement.get(2).html()); - } catch (IOException | IndexOutOfBoundsException e) { + statusCount = countElement.get(0).html(); + followingCount = countElement.get(1).html(); + followersCount = countElement.get(2).html(); + } catch (Exception e) { error = true; + e.printStackTrace(); } return null; } @Override protected void onPostExecute(Void result) { - listener.onRetrieveRemoteAccount(error, name, username, islocked, avatar, bio, statusCount, followingCount, followersCount); + listener.onRetrieveRemoteAccount(error, name, username, instance, islocked, avatar, bio, statusCount, followingCount, followersCount); } } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveScheduledTootsAsyncTask.java b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveScheduledTootsAsyncTask.java index e343d011c..d0cf7203c 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveScheduledTootsAsyncTask.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveScheduledTootsAsyncTask.java @@ -18,6 +18,7 @@ import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.os.AsyncTask; import android.os.SystemClock; +import android.util.Log; import com.evernote.android.job.JobManager; import com.evernote.android.job.JobRequest; @@ -68,6 +69,7 @@ public class RetrieveScheduledTootsAsyncTask extends AsyncTask }else{ jobIds = new int[]{}; } + if( storedStatuses != null && storedStatuses.size() > 0 ){ for(StoredStatus ss: storedStatuses){ if (!Helper.isJobPresent(jobIds, ss.getJobId())){ diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/client/API.java b/app/src/main/java/fr/gouv/etalab/mastodon/client/API.java index baa7b2d60..b16de7a9a 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/client/API.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/client/API.java @@ -188,12 +188,12 @@ public class API { get("/accounts/verify_credentials", null, new JsonHttpResponseHandler() { @Override public void onSuccess(int statusCode, Header[] headers, JSONObject response) { - account = parseAccountResponse(response); + account = parseAccountResponse(context, response); } @Override public void onSuccess(int statusCode, Header[] headers, JSONArray response) { try { - account = parseAccountResponse(response.getJSONObject(0)); + account = parseAccountResponse(context, response.getJSONObject(0)); } catch (JSONException e) { e.printStackTrace(); } @@ -217,12 +217,12 @@ public class API { get(String.format("/accounts/%s",accountId), null, new JsonHttpResponseHandler() { @Override public void onSuccess(int statusCode, Header[] headers, JSONObject response) { - account = parseAccountResponse(response); + account = parseAccountResponse(context, response); } @Override public void onSuccess(int statusCode, Header[] headers, JSONArray response) { try { - account = parseAccountResponse(response.getJSONObject(0)); + account = parseAccountResponse(context, response.getJSONObject(0)); } catch (JSONException e) { e.printStackTrace(); } @@ -330,7 +330,7 @@ public class API { get(String.format("/accounts/%s/statuses", accountId), params, new JsonHttpResponseHandler() { @Override public void onSuccess(int statusCode, Header[] headers, JSONObject response) { - Status status = parseStatuses(response); + Status status = parseStatuses(context, response); statuses.add(status); apiResponse.setSince_id(findSinceId(headers)); apiResponse.setMax_id(findMaxId(headers)); @@ -365,7 +365,7 @@ public class API { public void onSuccess(int statusCode, Header[] headers, JSONObject response) { apiResponse.setSince_id(findSinceId(headers)); apiResponse.setMax_id(findMaxId(headers)); - Status status = parseStatuses(response); + Status status = parseStatuses(context, response); statuses.add(status); } @Override @@ -445,7 +445,7 @@ public class API { @Override public void onSuccess(int statusCode, Header[] headers, JSONObject response) { - Status status = parseStatuses(response); + Status status = parseStatuses(context, response); statuses.add(status); apiResponse.setSince_id(findSinceId(headers)); apiResponse.setMax_id(findMaxId(headers)); @@ -500,7 +500,7 @@ public class API { @Override public void onSuccess(int statusCode, Header[] headers, JSONObject response) { - Status status = parseStatuses(response); + Status status = parseStatuses(context, response); statuses.add(status); apiResponse.setSince_id(findSinceId(headers)); apiResponse.setMax_id(findMaxId(headers)); @@ -557,7 +557,7 @@ public class API { @Override public void onSuccess(int statusCode, Header[] headers, JSONObject response) { - Status status = parseStatuses(response); + Status status = parseStatuses(context, response); statuses.add(status); apiResponse.setSince_id(findSinceId(headers)); apiResponse.setMax_id(findMaxId(headers)); @@ -641,7 +641,7 @@ public class API { public void onSuccess(int statusCode, Header[] headers, JSONObject response) { apiResponse.setSince_id(findSinceId(headers)); apiResponse.setMax_id(findMaxId(headers)); - Account account = parseAccountResponse(response); + Account account = parseAccountResponse(context, response); accounts.add(account); } @Override @@ -692,7 +692,7 @@ public class API { public void onSuccess(int statusCode, Header[] headers, JSONObject response) { apiResponse.setSince_id(findSinceId(headers)); apiResponse.setMax_id(findMaxId(headers)); - Account account = parseAccountResponse(response); + Account account = parseAccountResponse(context, response); accounts.add(account); } @Override @@ -741,7 +741,7 @@ public class API { @Override public void onSuccess(int statusCode, Header[] headers, JSONObject response) { - Status status = parseStatuses(response); + Status status = parseStatuses(context, response); statuses.add(status); apiResponse.setSince_id(findSinceId(headers)); apiResponse.setMax_id(findMaxId(headers)); @@ -945,7 +945,7 @@ public class API { @Override public void onSuccess(int statusCode, Header[] headers, JSONObject response) { - Status statusreturned = parseStatuses(response); + Status statusreturned = parseStatuses(context, response); statuses.add(statusreturned); apiResponse.setSince_id(findSinceId(headers)); apiResponse.setMax_id(findMaxId(headers)); @@ -1041,7 +1041,7 @@ public class API { @Override public void onSuccess(int statusCode, Header[] headers, JSONObject response) { - Notification notification = parseNotificationResponse(response); + Notification notification = parseNotificationResponse(context, response); notifications.add(notification); apiResponse.setSince_id(findSinceId(headers)); apiResponse.setMax_id(findMaxId(headers)); @@ -1118,37 +1118,6 @@ public class API { return results; } - /** - * Retrieves Developer account when searching (ie: via @...) *synchronously* - * - * @return APIResponse - */ - public APIResponse searchDeveloper() { - RequestParams params = new RequestParams(); - params.add("q", "tschneider"); - get("/accounts/search", params, new JsonHttpResponseHandler() { - @Override - public void onSuccess(int statusCode, Header[] headers, JSONObject response) { - accounts = new ArrayList<>(); - account = parseAccountResponse(response); - accounts.add(account); - apiResponse.setSince_id(findSinceId(headers)); - apiResponse.setMax_id(findMaxId(headers)); - } - @Override - public void onSuccess(int statusCode, Header[] headers, JSONArray response) { - accounts = parseDeveloperResponse(response); - apiResponse.setSince_id(findSinceId(headers)); - apiResponse.setMax_id(findMaxId(headers)); - } - @Override - public void onFailure(int statusCode, Header[] headers, Throwable error, JSONObject response){ - setError(statusCode, error); - } - }); - apiResponse.setAccounts(accounts); - return apiResponse; - } /** * Retrieves Accounts when searching (ie: via @...) *synchronously* * @@ -1170,7 +1139,7 @@ public class API { @Override public void onSuccess(int statusCode, Header[] headers, JSONObject response) { accounts = new ArrayList<>(); - account = parseAccountResponse(response); + account = parseAccountResponse(context, response); accounts.add(account); apiResponse.setSince_id(findSinceId(headers)); apiResponse.setMax_id(findMaxId(headers)); @@ -1236,7 +1205,7 @@ public class API { while (i < jsonArray.length() ){ JSONObject resobj = jsonArray.getJSONObject(i); - Status status = parseStatuses(resobj); + Status status = parseStatuses(context, resobj); i++; statuses.add(status); } @@ -1253,7 +1222,7 @@ public class API { * @return Status */ @SuppressWarnings("InfiniteRecursion") - private Status parseStatuses(JSONObject resobj){ + public static Status parseStatuses(Context context, JSONObject resobj){ Status status = new Status(); try { status.setId(resobj.get("id").toString()); @@ -1315,14 +1284,14 @@ public class API { } status.setTags(tags); - status.setAccount(parseAccountResponse(resobj.getJSONObject("account"))); + status.setAccount(parseAccountResponse(context, resobj.getJSONObject("account"))); status.setContent(resobj.get("content").toString()); status.setFavourites_count(Integer.valueOf(resobj.get("favourites_count").toString())); status.setReblogs_count(Integer.valueOf(resobj.get("reblogs_count").toString())); status.setReblogged(Boolean.valueOf(resobj.get("reblogged").toString())); status.setFavourited(Boolean.valueOf(resobj.get("favourited").toString())); try{ - status.setReblog(parseStatuses(resobj.getJSONObject("reblog"))); + status.setReblog(parseStatuses(context, resobj.getJSONObject("reblog"))); }catch (Exception ignored){} } catch (JSONException e) { e.printStackTrace(); @@ -1355,7 +1324,7 @@ public class API { * @param resobj JSONObject * @return Account */ - private Account parseAccountResponse(JSONObject resobj){ + private static Account parseAccountResponse(Context context, JSONObject resobj){ Account account = new Account(); try { @@ -1392,7 +1361,7 @@ public class API { int i = 0; while (i < jsonArray.length() ) { JSONObject resobj = jsonArray.getJSONObject(i); - Account account = parseAccountResponse(resobj); + Account account = parseAccountResponse(context, resobj); accounts.add(account); i++; } @@ -1416,7 +1385,7 @@ public class API { Account account = null; while (i < jsonArray.length() ) { JSONObject resobj = jsonArray.getJSONObject(i); - account = parseAccountResponse(resobj); + account = parseAccountResponse(context, resobj); if( account.getAcct().contains(Helper.DEVELOPER_INSTANCE)) accounts.add(account); i++; @@ -1500,16 +1469,16 @@ public class API { * @param resobj JSONObject * @return Account */ - private Notification parseNotificationResponse(JSONObject resobj){ + public static Notification parseNotificationResponse(Context context, JSONObject resobj){ Notification notification = new Notification(); try { notification.setId(resobj.get("id").toString()); notification.setType(resobj.get("type").toString()); notification.setCreated_at(Helper.mstStringToDate(context, resobj.get("created_at").toString())); - notification.setAccount(parseAccountResponse(resobj.getJSONObject("account"))); + notification.setAccount(parseAccountResponse(context, resobj.getJSONObject("account"))); try{ - notification.setStatus(parseStatuses(resobj.getJSONObject("status"))); + notification.setStatus(parseStatuses(context, resobj.getJSONObject("status"))); }catch (Exception ignored){} notification.setCreated_at(Helper.mstStringToDate(context, resobj.get("created_at").toString())); } catch (JSONException e) { @@ -1531,7 +1500,7 @@ public class API { while (i < jsonArray.length() ) { JSONObject resobj = jsonArray.getJSONObject(i); - Notification notification = parseNotificationResponse(resobj); + Notification notification = parseNotificationResponse(context, resobj); notifications.add(notification); i++; } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/client/Entities/Account.java b/app/src/main/java/fr/gouv/etalab/mastodon/client/Entities/Account.java index de8cdadb6..a587a3e63 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/client/Entities/Account.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/client/Entities/Account.java @@ -35,6 +35,9 @@ public class Account implements Parcelable { private int followers_count; private int following_count; private int statuses_count; + private String followers_count_str; + private String following_count_str; + private String statuses_count_str; private String note; private String url; private String avatar; @@ -43,6 +46,8 @@ public class Account implements Parcelable { private String header_static; private String token; private String instance; + private boolean isFollowing; + private boolean isRemote; protected Account(Parcel in) { id = in.readString(); @@ -237,4 +242,44 @@ public class Account implements Parcelable { dest.writeString(token); dest.writeString(instance); } + + public boolean isFollowing() { + return isFollowing; + } + + public void setFollowing(boolean following) { + isFollowing = following; + } + + public boolean isRemote() { + return isRemote; + } + + public void setRemote(boolean remote) { + isRemote = remote; + } + + public String getFollowers_count_str() { + return followers_count_str; + } + + public void setFollowers_count_str(String followers_count_str) { + this.followers_count_str = followers_count_str; + } + + public String getFollowing_count_str() { + return following_count_str; + } + + public void setFollowing_count_str(String following_count_str) { + this.following_count_str = following_count_str; + } + + public String getStatuses_count_str() { + return statuses_count_str; + } + + public void setStatuses_count_str(String statuses_count_str) { + this.statuses_count_str = statuses_count_str; + } } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/client/Entities/Notification.java b/app/src/main/java/fr/gouv/etalab/mastodon/client/Entities/Notification.java index a8cc8f74d..c415088dc 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/client/Entities/Notification.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/client/Entities/Notification.java @@ -15,13 +15,16 @@ package fr.gouv.etalab.mastodon.client.Entities; +import android.os.Parcel; +import android.os.Parcelable; + import java.util.Date; /** * Created by Thomas on 23/04/2017. */ -public class Notification { +public class Notification implements Parcelable { private String id; private String type; @@ -29,6 +32,27 @@ public class Notification { private Account account; private Status status; + protected Notification(Parcel in) { + id = in.readString(); + type = in.readString(); + account = in.readParcelable(Account.class.getClassLoader()); + status = in.readParcelable(Status.class.getClassLoader()); + } + + public Notification(){}; + + public static final Creator CREATOR = new Creator() { + @Override + public Notification createFromParcel(Parcel in) { + return new Notification(in); + } + + @Override + public Notification[] newArray(int size) { + return new Notification[size]; + } + }; + public String getId() { return id; } @@ -68,4 +92,17 @@ public class Notification { public void setStatus(Status status) { this.status = status; } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(id); + dest.writeString(type); + dest.writeParcelable(account, flags); + dest.writeParcelable(status, flags); + } } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/client/Entities/Status.java b/app/src/main/java/fr/gouv/etalab/mastodon/client/Entities/Status.java index 4fd0cc909..1901c5ef7 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/client/Entities/Status.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/client/Entities/Status.java @@ -55,6 +55,7 @@ public class Status implements Parcelable { private String language; private boolean isTranslated = false; private boolean isTranslationShown = false; + private boolean isNew = false; protected Status(Parcel in) { id = in.readString(); @@ -79,6 +80,7 @@ public class Status implements Parcelable { spoilerShown = in.readByte() != 0; isTranslated = in.readByte() != 0; isTranslationShown = in.readByte() != 0; + isNew = in.readByte() != 0; } public Status(){} @@ -294,6 +296,7 @@ public class Status implements Parcelable { dest.writeByte((byte) (spoilerShown ? 1 : 0)); dest.writeByte((byte) (isTranslated ? 1 : 0)); dest.writeByte((byte) (isTranslationShown ? 1 : 0)); + dest.writeByte((byte) (isNew ? 1 : 0)); } public boolean isSpoilerShown() { @@ -343,4 +346,12 @@ public class Status implements Parcelable { public void setReplies(List replies) { this.replies = replies; } + + public boolean isNew() { + return isNew; + } + + public void setNew(boolean aNew) { + isNew = aNew; + } } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/client/TLSSocketFactory.java b/app/src/main/java/fr/gouv/etalab/mastodon/client/TLSSocketFactory.java new file mode 100644 index 000000000..6cd21710f --- /dev/null +++ b/app/src/main/java/fr/gouv/etalab/mastodon/client/TLSSocketFactory.java @@ -0,0 +1,94 @@ +package fr.gouv.etalab.mastodon.client; + +import android.annotation.SuppressLint; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +/** + * Created by Thomas on 29/08/2017. + * + */ + +public class TLSSocketFactory extends SSLSocketFactory { + + private final SSLContext sslContext = SSLContext.getInstance("TLS"); + + public TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException { + + X509TrustManager tm = new X509TrustManager() { + @SuppressLint("TrustAllX509TrustManager") + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + + @SuppressLint("TrustAllX509TrustManager") + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + } + + public X509Certificate[] getAcceptedIssuers() { + return null; + } + }; + sslContext.init(null, new TrustManager[]{tm}, null); + + } + + @Override + public String[] getDefaultCipherSuites() { + return sslContext.getSocketFactory().getDefaultCipherSuites(); + } + + @Override + public String[] getSupportedCipherSuites() { + return sslContext.getSocketFactory().getSupportedCipherSuites(); + } + + @Override + public Socket createSocket() throws IOException { + return enableTLSOnSocket(sslContext.getSocketFactory().createSocket()); + } + + @Override + public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { + return enableTLSOnSocket(sslContext.getSocketFactory().createSocket(s, host, port, autoClose)); + } + + @Override + public Socket createSocket(String host, int port) throws IOException, UnknownHostException { + return enableTLSOnSocket(sslContext.getSocketFactory().createSocket(host, port)); + } + + @Override + public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException { + return enableTLSOnSocket(sslContext.getSocketFactory().createSocket(host, port, localHost, localPort)); + } + + @Override + public Socket createSocket(InetAddress host, int port) throws IOException { + return enableTLSOnSocket(sslContext.getSocketFactory().createSocket(host, port)); + } + + @Override + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { + return enableTLSOnSocket(sslContext.getSocketFactory().createSocket(address, port, localAddress, localPort)); + } + + private Socket enableTLSOnSocket(Socket socket) { + if(socket != null && (socket instanceof SSLSocket)) { + ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"}); + } + return socket; + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/drawers/AccountSearchDevAdapter.java b/app/src/main/java/fr/gouv/etalab/mastodon/drawers/AccountSearchDevAdapter.java new file mode 100644 index 000000000..993a445b4 --- /dev/null +++ b/app/src/main/java/fr/gouv/etalab/mastodon/drawers/AccountSearchDevAdapter.java @@ -0,0 +1,213 @@ +package fr.gouv.etalab.mastodon.drawers; +/* Copyright 2017 Thomas Schneider + * + * This file is a part of Mastalab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Mastalab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Mastalab; if not, + * see . */ + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.graphics.drawable.Drawable; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.support.design.widget.FloatingActionButton; +import android.support.v4.content.ContextCompat; +import android.text.Html; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; + +import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache; +import com.nostra13.universalimageloader.core.DisplayImageOptions; +import com.nostra13.universalimageloader.core.ImageLoader; +import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; +import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer; + +import java.io.File; +import java.util.List; + +import fr.gouv.etalab.mastodon.activities.ShowAccountActivity; +import fr.gouv.etalab.mastodon.asynctasks.PostActionAsyncTask; +import fr.gouv.etalab.mastodon.client.API; +import fr.gouv.etalab.mastodon.client.Entities.Account; +import fr.gouv.etalab.mastodon.client.Entities.Error; +import fr.gouv.etalab.mastodon.client.PatchBaseImageDownloader; +import fr.gouv.etalab.mastodon.helper.Helper; +import fr.gouv.etalab.mastodon.interfaces.OnPostActionInterface; +import mastodon.etalab.gouv.fr.mastodon.R; + +import static fr.gouv.etalab.mastodon.helper.Helper.changeDrawableColor; + + +/** + * Created by Thomas on 03/09/2017. + * Adapter for accounts from web + */ +public class AccountSearchDevAdapter extends BaseAdapter implements OnPostActionInterface { + + private List accounts; + private LayoutInflater layoutInflater; + private Context context; + private ViewHolder holder; + + public AccountSearchDevAdapter(Context context, List accounts){ + this.context = context; + this.accounts = accounts; + layoutInflater = LayoutInflater.from(context); + } + + + + @Override + public int getCount() { + return accounts.size(); + } + + @Override + public Object getItem(int position) { + return accounts.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + + @Override + public View getView(final int position, View convertView, ViewGroup parent) { + + ImageLoader imageLoader = ImageLoader.getInstance(); + File cacheDir = new File(context.getCacheDir(), context.getString(R.string.app_name)); + ImageLoaderConfiguration configImg = new ImageLoaderConfiguration.Builder(context) + .imageDownloader(new PatchBaseImageDownloader(context)) + .threadPoolSize(5) + .threadPriority(Thread.MIN_PRIORITY + 3) + .denyCacheImageMultipleSizesInMemory() + .diskCache(new UnlimitedDiskCache(cacheDir)) + .build(); + if( !imageLoader.isInited()) + imageLoader.init(configImg); + DisplayImageOptions options = new DisplayImageOptions.Builder().displayer(new SimpleBitmapDisplayer()).cacheInMemory(false) + .cacheOnDisk(true).resetViewBeforeLoading(true).build(); + final Account account = accounts.get(position); + + if (convertView == null) { + convertView = layoutInflater.inflate(R.layout.drawer_account_search_dev, parent, false); + holder = new ViewHolder(); + holder.account_pp = (ImageView) convertView.findViewById(R.id.account_pp); + holder.account_dn = (TextView) convertView.findViewById(R.id.account_dn); + holder.account_un = (TextView) convertView.findViewById(R.id.account_un); + holder.account_follow = (FloatingActionButton) convertView.findViewById(R.id.account_follow); + holder.acccount_container = (LinearLayout) convertView.findViewById(R.id.acccount_container); + convertView.setTag(holder); + } else { + holder = (ViewHolder) convertView.getTag(); + } + //Redraws icon for locked accounts + final float scale = context.getResources().getDisplayMetrics().density; + if( account != null && account.isLocked()){ + Drawable img = ContextCompat.getDrawable(context, R.drawable.ic_action_lock_closed); + img.setBounds(0,0,(int) (20 * scale + 0.5f),(int) (20 * scale + 0.5f)); + holder.account_dn.setCompoundDrawables( null, null, img, null); + }else{ + holder.account_dn.setCompoundDrawables( null, null, null, null); + } + + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + holder.account_dn.setText(Html.fromHtml(Helper.shortnameToUnicode(account.getDisplay_name(), true), Html.FROM_HTML_MODE_LEGACY)); + holder.account_un.setText(Html.fromHtml(Helper.shortnameToUnicode(account.getUsername(), true), Html.FROM_HTML_MODE_LEGACY)); + }else { + //noinspection deprecation + holder.account_dn.setText(Html.fromHtml(Helper.shortnameToUnicode(account.getDisplay_name(), true))); + holder.account_un.setText(Html.fromHtml(Helper.shortnameToUnicode(account.getUsername(), true))); + } + changeDrawableColor(context, R.drawable.ic_action_lock_closed,R.color.mastodonC4); + //Profile picture + imageLoader.displayImage(account.getAvatar(), holder.account_pp, options); + + if( account.isFollowing()){ + holder.account_follow.setVisibility(View.GONE); + }else{ + holder.account_follow.setVisibility(View.VISIBLE); + } + + if( account.isRemote()) { + holder.account_follow.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + holder.account_follow.setEnabled(false); + new PostActionAsyncTask(context, API.StatusAction.REMOTE_FOLLOW, account.getAcct(), AccountSearchDevAdapter.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + }); + }else { + holder.account_follow.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + holder.account_follow.setEnabled(false); + new PostActionAsyncTask(context, API.StatusAction.FOLLOW, account.getId(), AccountSearchDevAdapter.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + }); + holder.acccount_container.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(context, ShowAccountActivity.class); + Bundle b = new Bundle(); + b.putString("accountId", account.getId()); + intent.putExtras(b); + context.startActivity(intent); + } + }); + } + + return convertView; + } + + @Override + public void onPostAction(int statusCode, API.StatusAction statusAction, String userId, Error error) { + if( error != null){ + final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + boolean show_error_messages = sharedpreferences.getBoolean(Helper.SET_SHOW_ERROR_MESSAGES, true); + if( show_error_messages) + Toast.makeText(context, error.getError(),Toast.LENGTH_LONG).show(); + holder.account_follow.setEnabled(true); + return; + } + for( Account account: accounts){ + if(account.getId().equals(userId)) { + account.setFollowing(true); + notifyDataSetChanged(); + break; + } + } + holder.account_follow.setVisibility(View.GONE); + Toast.makeText(context, R.string.toast_follow, Toast.LENGTH_LONG).show(); + } + + + private class ViewHolder { + LinearLayout acccount_container; + ImageView account_pp; + TextView account_dn; + TextView account_un; + FloatingActionButton account_follow; + } + +} \ No newline at end of file diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/drawers/AccountSearchWebAdapter.java b/app/src/main/java/fr/gouv/etalab/mastodon/drawers/AccountSearchWebAdapter.java index d29923a48..44decec34 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/drawers/AccountSearchWebAdapter.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/drawers/AccountSearchWebAdapter.java @@ -143,9 +143,9 @@ public class AccountSearchWebAdapter extends BaseAdapter implements OnPostAction } changeDrawableColor(context, R.drawable.ic_action_lock_closed,R.color.mastodonC4); holder.account_ds.setAutoLinkMask(Linkify.WEB_URLS); - holder.account_sc.setText(String.valueOf(account.getStatuses_count())); - holder.account_fgc.setText(String.valueOf(account.getFollowing_count())); - holder.account_frc.setText(String.valueOf(account.getFollowers_count())); + holder.account_sc.setText(String.valueOf(account.getStatuses_count_str())); + holder.account_fgc.setText(String.valueOf(account.getFollowing_count_str())); + holder.account_frc.setText(String.valueOf(account.getFollowers_count_str())); //Profile picture imageLoader.displayImage(account.getAvatar(), holder.account_pp, options); diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/drawers/NotificationsListAdapter.java b/app/src/main/java/fr/gouv/etalab/mastodon/drawers/NotificationsListAdapter.java index aa91b19b1..7c41982b2 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/drawers/NotificationsListAdapter.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/drawers/NotificationsListAdapter.java @@ -88,7 +88,7 @@ public class NotificationsListAdapter extends BaseAdapter implements OnPostActio private NotificationsListAdapter notificationsListAdapter; private int behaviorWithAttachments; private boolean isOnWifi; - private String targetedId; + public NotificationsListAdapter(Context context, boolean isOnWifi, int behaviorWithAttachments, List notifications){ this.context = context; @@ -170,6 +170,8 @@ public class NotificationsListAdapter extends BaseAdapter implements OnPostActio final float scale = context.getResources().getDisplayMetrics().density; String type = notification.getType(); String typeString = ""; + int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK); + Drawable imgH = null; switch (type){ case "mention": holder.status_action_container.setVisibility(View.VISIBLE); @@ -177,6 +179,12 @@ public class NotificationsListAdapter extends BaseAdapter implements OnPostActio typeString = String.format("%s %s", Helper.shortnameToUnicode(notification.getAccount().getDisplay_name(), true),context.getString(R.string.notif_mention)); else typeString = String.format("@%s %s", notification.getAccount().getAcct(),context.getString(R.string.notif_mention)); + if( theme == Helper.THEME_DARK){ + holder.card_status_container.setCardBackgroundColor(ContextCompat.getColor(context, R.color.notif_dark_1)); + }else { + holder.card_status_container.setCardBackgroundColor(ContextCompat.getColor(context, R.color.notif_light_1)); + } + imgH = null; break; case "reblog": holder.status_action_container.setVisibility(View.GONE); @@ -184,6 +192,12 @@ public class NotificationsListAdapter extends BaseAdapter implements OnPostActio typeString = String.format("%s %s", Helper.shortnameToUnicode(notification.getAccount().getDisplay_name(), true),context.getString(R.string.notif_reblog)); else typeString = String.format("@%s %s", notification.getAccount().getAcct(),context.getString(R.string.notif_reblog)); + if( theme == Helper.THEME_DARK){ + holder.card_status_container.setCardBackgroundColor(ContextCompat.getColor(context, R.color.notif_dark_2)); + }else { + holder.card_status_container.setCardBackgroundColor(ContextCompat.getColor(context, R.color.notif_light_2)); + } + imgH = ContextCompat.getDrawable(context, R.drawable.ic_retweet_notif_header); break; case "favourite": holder.status_action_container.setVisibility(View.GONE); @@ -191,6 +205,12 @@ public class NotificationsListAdapter extends BaseAdapter implements OnPostActio typeString = String.format("%s %s", Helper.shortnameToUnicode(notification.getAccount().getDisplay_name(), true),context.getString(R.string.notif_favourite)); else typeString = String.format("@%s %s", notification.getAccount().getAcct(),context.getString(R.string.notif_favourite)); + if( theme == Helper.THEME_DARK){ + holder.card_status_container.setCardBackgroundColor(ContextCompat.getColor(context, R.color.notif_dark_3)); + }else { + holder.card_status_container.setCardBackgroundColor(ContextCompat.getColor(context, R.color.notif_light_3)); + } + imgH = ContextCompat.getDrawable(context, R.drawable.ic_fav_notif_header); break; case "follow": holder.status_action_container.setVisibility(View.GONE); @@ -198,10 +218,24 @@ public class NotificationsListAdapter extends BaseAdapter implements OnPostActio typeString = String.format("%s %s", Helper.shortnameToUnicode(notification.getAccount().getDisplay_name(), true),context.getString(R.string.notif_follow)); else typeString = String.format("@%s %s", notification.getAccount().getAcct(),context.getString(R.string.notif_follow)); + if( theme == Helper.THEME_DARK){ + holder.card_status_container.setCardBackgroundColor(ContextCompat.getColor(context, R.color.notif_dark_4)); + }else { + holder.card_status_container.setCardBackgroundColor(ContextCompat.getColor(context, R.color.notif_light_4)); + } + imgH = ContextCompat.getDrawable(context, R.drawable.ic_follow_notif_header); break; } - + changeDrawableColor(context, R.drawable.ic_retweet_notif_header,R.color.mastodonC4); + changeDrawableColor(context, R.drawable.ic_fav_notif_header,R.color.mastodonC4); + changeDrawableColor(context, R.drawable.ic_follow_notif_header,R.color.mastodonC4); holder.notification_type.setText(typeString); + if( imgH != null) { + holder.notification_type.setCompoundDrawablePadding((int)Helper.convertDpToPixel(5, context)); + imgH.setBounds(0, 0, (int) (20 * iconSizePercent / 100 * scale + 0.5f), (int) (20 * iconSizePercent / 100 * scale + 0.5f)); + } + holder.notification_type.setCompoundDrawables( imgH, null, null, null); + holder.status_privacy.getLayoutParams().height = (int) Helper.convertDpToPixel((20*iconSizePercent/100), context); holder.status_privacy.getLayoutParams().width = (int) Helper.convertDpToPixel((20*iconSizePercent/100), context); holder.status_reply.getLayoutParams().height = (int) Helper.convertDpToPixel((20*iconSizePercent/100), context); @@ -214,7 +248,6 @@ public class NotificationsListAdapter extends BaseAdapter implements OnPostActio //Manages theme for icon colors - int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK); if( theme == Helper.THEME_DARK){ changeDrawableColor(context, R.drawable.ic_reply,R.color.dark_text); changeDrawableColor(context, R.drawable.ic_action_more,R.color.dark_text); @@ -260,8 +293,13 @@ public class NotificationsListAdapter extends BaseAdapter implements OnPostActio }else{ holder.notification_account_username.setCompoundDrawables( null, null, null, null); } + String content = status.getContent(); + content = content.replaceAll("

","

"); + content = content.replaceAll("

",""); + if( content.endsWith("

") ) + content = content.substring(0,content.length() -10); - SpannableString spannableString = Helper.clickableElements(context, status.getContent(), + SpannableString spannableString = Helper.clickableElements(context, content, status.getReblog() != null?status.getReblog().getMentions():status.getMentions(), true); holder.notification_status_content.setText(spannableString, TextView.BufferType.SPANNABLE); holder.notification_status_content.setMovementMethod(null); @@ -402,7 +440,7 @@ public class NotificationsListAdapter extends BaseAdapter implements OnPostActio @Override public void onClick(View v) { - boolean confirmation = sharedpreferences.getBoolean(Helper.SET_NOTIF_VALIDATION, true); + boolean confirmation = sharedpreferences.getBoolean(Helper.SET_NOTIF_VALIDATION_FAV, false); if( confirmation ) displayConfirmationDialog(FAVOURITE,status); else @@ -522,7 +560,7 @@ public class NotificationsListAdapter extends BaseAdapter implements OnPostActio */ private void displayConfirmationNotificationDialog(final Notification notification){ final ArrayList seletedItems = new ArrayList(); - final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + AlertDialog dialog = new AlertDialog.Builder(context) .setTitle(R.string.delete_notification_ask) .setMultiChoiceItems(new String[]{context.getString(R.string.delete_notification_ask_all)}, null, new DialogInterface.OnMultiChoiceClickListener() { diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/drawers/StatusListAdapter.java b/app/src/main/java/fr/gouv/etalab/mastodon/drawers/StatusListAdapter.java index 6774963d2..57fb935bb 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/drawers/StatusListAdapter.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/drawers/StatusListAdapter.java @@ -14,6 +14,7 @@ package fr.gouv.etalab.mastodon.drawers; * You should have received a copy of the GNU General Public License along with Mastalab; if not, * see . */ +import android.graphics.Paint; import android.support.v7.app.AlertDialog; import android.content.ClipData; import android.content.ClipboardManager; @@ -90,6 +91,7 @@ import mastodon.etalab.gouv.fr.mastodon.R; import static fr.gouv.etalab.mastodon.activities.MainActivity.currentLocale; import static fr.gouv.etalab.mastodon.helper.Helper.changeDrawableColor; +import static fr.gouv.etalab.mastodon.helper.Helper.shortnameToUnicode; /** @@ -197,7 +199,7 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf holder.status_prev4_container = (RelativeLayout) convertView.findViewById(R.id.status_prev4_container); holder.status_reply = (ImageView) convertView.findViewById(R.id.status_reply); holder.status_privacy = (ImageView) convertView.findViewById(R.id.status_privacy); - holder.status_translate = (Button) convertView.findViewById(R.id.status_translate); + holder.status_translate = (TextView) convertView.findViewById(R.id.status_translate); holder.status_content_translated_container = (LinearLayout) convertView.findViewById(R.id.status_content_translated_container); holder.main_container = (LinearLayout) convertView.findViewById(R.id.main_container); holder.status_spoiler_container = (LinearLayout) convertView.findViewById(R.id.status_spoiler_container); @@ -205,9 +207,11 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf holder.status_spoiler = (TextView) convertView.findViewById(R.id.status_spoiler); holder.status_spoiler_button = (Button) convertView.findViewById(R.id.status_spoiler_button); holder.yandex_translate = (TextView) convertView.findViewById(R.id.yandex_translate); + holder.google_translate = (TextView) convertView.findViewById(R.id.google_translate); holder.status_replies = (LinearLayout) convertView.findViewById(R.id.status_replies); holder.status_replies_profile_pictures = (LinearLayout) convertView.findViewById(R.id.status_replies_profile_pictures); holder.status_replies_text = (TextView) convertView.findViewById(R.id.status_replies_text); + holder.new_element = (ImageView) convertView.findViewById(R.id.new_element); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); @@ -217,7 +221,7 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf //Display a preview for accounts that have replied *if enabled and only for home timeline* if( type == RetrieveFeedsAsyncTask.Type.HOME ) { - boolean showPreview = sharedpreferences.getBoolean(Helper.SET_PREVIEW_REPLIES, true); + boolean showPreview = sharedpreferences.getBoolean(Helper.SET_PREVIEW_REPLIES, false); if( showPreview){ boolean showPreviewPP = sharedpreferences.getBoolean(Helper.SET_PREVIEW_REPLIES_PP, true); if( status.getReplies() == null){ @@ -259,6 +263,11 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf } } + changeDrawableColor(context, R.drawable.ic_fiber_new,R.color.mastodonC4); + if( status.isNew()) + holder.new_element.setVisibility(View.VISIBLE); + else + holder.new_element.setVisibility(View.GONE); int iconSizePercent = sharedpreferences.getInt(Helper.SET_ICON_SIZE, 130); int textSizePercent = sharedpreferences.getInt(Helper.SET_TEXT_SIZE, 110); @@ -301,6 +310,7 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf statusListAdapter.notifyDataSetChanged(); } }); + holder.status_translate.setPaintFlags(holder.status_translate.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG); if( currentLocale != null && status.getLanguage() != null && !status.getLanguage().trim().equals(currentLocale) && !status.getLanguage().trim().equals("null")){ if (translator != Helper.TRANS_NONE) holder.status_translate.setVisibility(View.VISIBLE); @@ -310,10 +320,25 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf holder.status_translate.setVisibility(View.GONE); } - if( translator == Helper.TRANS_YANDEX) - holder.yandex_translate.setVisibility(View.VISIBLE); - else - holder.yandex_translate.setVisibility(View.GONE); + switch (translator) + { + case Helper.TRANS_NONE: + holder.yandex_translate.setVisibility(View.GONE); + holder.google_translate.setVisibility(View.GONE); + break; + case Helper.TRANS_YANDEX: + holder.google_translate.setVisibility(View.GONE); + holder.yandex_translate.setVisibility(View.VISIBLE); + break; + case Helper.TRANS_GOOGLE: + holder.yandex_translate.setVisibility(View.GONE); + holder.google_translate.setVisibility(View.VISIBLE); + break; + default: + holder.yandex_translate.setVisibility(View.GONE); + holder.google_translate.setVisibility(View.GONE); + break; + } holder.status_translate.setOnClickListener(new View.OnClickListener() { @Override @@ -340,6 +365,11 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf while (matcher.find()){ String key = "__u" + String.valueOf(i) + "__"; String value = matcher.group(0); + int end = matcher.end(); + if (spannableString.charAt(end) == '/') { + text = spannableString.toString().substring(0, end). + concat(spannableString.toString().substring(end+1, spannableString.length())); + } if( value != null) { urlConversion.put(key, value); text = text.replace(value, key); @@ -361,9 +391,13 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf } if (translator == Helper.TRANS_YANDEX) new YandexQuery(StatusListAdapter.this).getYandexTextview(position, text, currentLocale); - else if( translator == Helper.TRANS_GOOGLE) - new GoogleTranslateQuery(StatusListAdapter.this).getGoogleTextview(position, text, currentLocale); + else if( translator == Helper.TRANS_GOOGLE) { + while( text.charAt(text.length() -1) == '\n' && text.length() > 0) + text = text.substring(0, text.length() -1); + text += "."; + new GoogleTranslateQuery(StatusListAdapter.this).getGoogleTextview(position, text.trim(), currentLocale); + } }else { status.setTranslationShown(!status.isTranslationShown()); statusListAdapter.notifyDataSetChanged(); @@ -381,6 +415,13 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf context.startActivity(browserIntent); } }); + holder.google_translate.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://translate.google.com/")); + context.startActivity(browserIntent); + } + }); //Toot was translated and user asked to see it if( status.isTranslationShown()){ holder.status_content.setVisibility(View.GONE); @@ -480,7 +521,10 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf } } - final String content, displayName, username, ppurl; + String content; + final String displayName; + final String username; + final String ppurl; if( status.getReblog() != null){ content = status.getReblog().getContent(); displayName = Helper.shortnameToUnicode(status.getReblog().getAccount().getDisplay_name(), true); @@ -546,7 +590,10 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf }); holder.status_content_translated.setMovementMethod(LinkMovementMethod.getInstance()); } - + content = content.replaceAll("

","

"); + content = content.replaceAll("

",""); + if( content.endsWith("

") ) + content = content.substring(0,content.length() -10); final SpannableString spannableString = Helper.clickableElements(context,content, status.getReblog() != null?status.getReblog().getMentions():status.getMentions(), true); holder.status_content.setText(spannableString, TextView.BufferType.SPANNABLE); @@ -712,7 +759,7 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf @Override public void onClick(View v) { - boolean confirmation = sharedpreferences.getBoolean(Helper.SET_NOTIF_VALIDATION, true); + boolean confirmation = sharedpreferences.getBoolean(Helper.SET_NOTIF_VALIDATION_FAV, false); if( confirmation ) displayConfirmationDialog(FAVOURITE,status); else @@ -956,6 +1003,7 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf statuses.get(position).setContent_translated(aJsonString); statusListAdapter.notifyDataSetChanged(); } catch (JSONException | UnsupportedEncodingException | IllegalArgumentException e) { + e.printStackTrace(); Toast.makeText(context, R.string.toast_error_translate, Toast.LENGTH_LONG).show(); } } @@ -965,6 +1013,16 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf JSONObject translationJson = new JSONObject(text); JSONArray aJsonArray = translationJson.getJSONArray("text"); String aJsonString = aJsonArray.get(0).toString(); + + /* The one instance where I've seen this happen, + the special tag was originally a hashtag ("__t1__"), + that Yandex decided to change to a "__q1 - __". + */ + aJsonString = aJsonString.replaceAll("__q(\\d+) - __", "__t$1__"); + + // Noticed this in the very same toot + aJsonString = aJsonString.replace("&", "&"); + aJsonString = URLDecoder.decode(aJsonString, "UTF-8"); return aJsonString; } @@ -988,6 +1046,19 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf aJsonString = aJsonString.replace(" //","//"); aJsonString = aJsonString.replace(" www .","www."); aJsonString = aJsonString.replace("www .","www."); + + // This one might cause more trouble than it's worth + aJsonString = aJsonString.replaceAll("\\* \\.", "*."); + + /* + Noticed that sometimes the special tags were getting messed up by Google, + might be other variants, only caught one so far. + + But, pre-planning might save some time later... + */ + aJsonString = aJsonString.replaceAll("__\\s?(u|t)\\s?(\\d+)\\s?__", "__$1$2__"); + aJsonString = aJsonString.replaceAll("%(?![0-9a-fA-F]{2})", "%25"); + aJsonString = aJsonString.replaceAll("\\+", "%2B"); aJsonString = URLDecoder.decode(aJsonString, "UTF-8"); return aJsonString; } @@ -1026,16 +1097,19 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf RelativeLayout status_prev4_container; ImageView status_reply; ImageView status_privacy; - Button status_translate; + TextView status_translate; LinearLayout status_container2; LinearLayout status_container3; LinearLayout main_container; TextView yandex_translate; + TextView google_translate; LinearLayout status_replies; LinearLayout status_replies_profile_pictures; TextView status_replies_text; LinearLayout loader_replies; + + ImageView new_element; } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayNotificationsFragment.java b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayNotificationsFragment.java index afa16939d..3eb6b8f42 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayNotificationsFragment.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayNotificationsFragment.java @@ -31,13 +31,14 @@ import android.widget.TextView; import android.widget.Toast; import java.util.ArrayList; -import java.util.Date; import java.util.List; import fr.gouv.etalab.mastodon.activities.MainActivity; import fr.gouv.etalab.mastodon.client.APIResponse; import fr.gouv.etalab.mastodon.client.Entities.Account; +import fr.gouv.etalab.mastodon.client.Entities.Status; import fr.gouv.etalab.mastodon.drawers.NotificationsListAdapter; +import fr.gouv.etalab.mastodon.drawers.StatusListAdapter; import fr.gouv.etalab.mastodon.helper.Helper; import fr.gouv.etalab.mastodon.sqlite.AccountDAO; import fr.gouv.etalab.mastodon.sqlite.Sqlite; @@ -66,12 +67,9 @@ public class DisplayNotificationsFragment extends Fragment implements OnRetrieve private SwipeRefreshLayout swipeRefreshLayout; private boolean swiped; private ListView lv_notifications; - private DisplayNotificationsFragment displayNotificationsFragment; private TextView new_data; - private String since_id; public DisplayNotificationsFragment(){ - displayNotificationsFragment = this; } @Override @@ -138,27 +136,28 @@ public class DisplayNotificationsFragment extends Fragment implements OnRetrieve new_data.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + notificationsTmp = Helper.getTempNotification(context, null); if( notificationsTmp != null){ + for(int i = notificationsTmp.size() -1 ; i >= 0 ; i--){ + notifications.add(0,notificationsTmp.get(i)); + } boolean isOnWifi = Helper.isOnWIFI(context); int behaviorWithAttachments = sharedpreferences.getInt(Helper.SET_ATTACHMENT_ACTION, Helper.ATTACHMENT_ALWAYS); - notifications = new ArrayList<>(); - for(Notification notification: notificationsTmp){ - notifications.add(notification); - } - //The user clicked on the banner to refresh values so, the pointer is changed - if( notificationsTmp.size() > 0 ) { - SharedPreferences.Editor editor = sharedpreferences.edit(); - String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); - editor.putString(Helper.LAST_MAX_ID_BUBBLE_NOTIF + userId, notificationsTmp.get(0).getId()); - editor.apply(); - } notificationsListAdapter = new NotificationsListAdapter(context,isOnWifi, behaviorWithAttachments, notifications); lv_notifications.setAdapter(notificationsListAdapter); + if( notificationsTmp.size() > 0){ + SharedPreferences.Editor editor = sharedpreferences.edit(); + String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); + editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + userId, notificationsTmp.get(0).getId()); + editor.apply(); + } if( notificationsTmp.size() > 0 && textviewNoAction.getVisibility() == View.VISIBLE) textviewNoAction.setVisibility(View.GONE); } new_data.setVisibility(View.GONE); notificationsTmp = new ArrayList<>(); + Helper.cacheNotificationsClear(context, null); + ((MainActivity) context).updateNotifCounter(); } }); @@ -188,11 +187,42 @@ public class DisplayNotificationsFragment extends Fragment implements OnRetrieve } + @Override + public void onResume() { + super.onResume(); + //New data are available + notificationsTmp = Helper.getTempNotification(context, null); + if (getUserVisibleHint() && notificationsTmp != null && notificationsTmp.size() > 0 && notifications.size() > 0) { + ArrayList added = new ArrayList<>(); + for(Notification notification : notifications){ + added.add(notification.getId()); + } + final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + boolean isOnWifi = Helper.isOnWIFI(context); + int behaviorWithAttachments = sharedpreferences.getInt(Helper.SET_ATTACHMENT_ACTION, Helper.ATTACHMENT_ALWAYS); + for(int i = notificationsTmp.size() -1 ; i >= 0 ; i--){ + if( !added.contains(notificationsTmp.get(i).getId())) { + this.notifications.add(0, notificationsTmp.get(i)); + added.add(notificationsTmp.get(i).getId()); + } + } + if( this.notifications.size() > 0 ) + max_id = this.notifications.get(this.notifications.size()-1).getId(); + notificationsListAdapter = new NotificationsListAdapter(context,isOnWifi, behaviorWithAttachments, notifications); + lv_notifications.setAdapter(notificationsListAdapter); + } + } + + @Override + public void setUserVisibleHint(boolean isVisibleToUser) { + super.setUserVisibleHint(isVisibleToUser); + if( isVisibleToUser ) + refresh(); + } + @Override public void onRetrieveNotifications(APIResponse apiResponse, String acct, String userId, boolean refreshData) { final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); - //UserId will be null here, so it needs to be retrieved from shared preferences - userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); mainLoader.setVisibility(View.GONE); nextElementLoader.setVisibility(View.GONE); if( apiResponse.getError() != null){ @@ -205,78 +235,34 @@ public class DisplayNotificationsFragment extends Fragment implements OnRetrieve return; } SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.LAST_BUBBLE_REFRESH_NOTIF+ userId,Helper.dateToString(context, new Date())); - editor.apply(); - String bubble_max_id = sharedpreferences.getString(Helper.LAST_MAX_ID_BUBBLE_NOTIF + userId, null); List notifications = apiResponse.getNotifications(); - since_id = apiResponse.getSince_id(); + String since_id = apiResponse.getSince_id(); max_id = apiResponse.getMax_id(); //The initial call comes from a classic tab refresh - if( refreshData ) { - manageNotifications(notifications, max_id, since_id); - //The current tab is displayed, so user is supposed to have seen the notifications - if( since_id != null && displayNotificationsFragment.getUserVisibleHint()) { - editor.putString(Helper.LAST_MAX_ID_BUBBLE_NOTIF + userId, since_id); - editor.apply(); - }else if(!displayNotificationsFragment.getUserVisibleHint()){ - //The refresh was done automatically, but the fragment was not displayed in viewpager - //So the bubble counter will be displayed - int countData = 0; - //Retrieves new notification count - if( bubble_max_id != null) { - for (Notification nt : notifications) { - if (nt.getId().trim().equals(bubble_max_id.trim())) - break; - countData++; - } - } - ((MainActivity)context).updateNotifCounter(countData); - } - }else { //Here, new values have been retrieved on the onResume call (forced mode) - int countData = 0; - if( bubble_max_id != null) { - for (Notification nt : notifications) { - if (nt.getId().trim().equals(bubble_max_id.trim())) - break; - countData++; - } - } - if( notifications != null && notifications.size() > 0 && countData > 0) { - max_id = null; - firstLoad = true; - notificationsTmp = new ArrayList<>(); - for (Notification tmpNotification : notifications) { - this.notificationsTmp.add(tmpNotification); - } - //New notifications will be counted - //The fragment is not displayed, so the bubble counter should be shown - if (!displayNotificationsFragment.getUserVisibleHint()) { - ((MainActivity) context).updateNotifCounter(countData); - } else { //The current fragment is visible, but for avoiding to populate with new values - // a message will be displayed at the bottom requiring a click to display these new values - new_data.setVisibility(View.VISIBLE); - } - } - } - } - - private void manageNotifications(List notifications, String max_id, String since_id){ flag_loading = (max_id == null ); - final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); if( !swiped && firstLoad && (notifications == null || notifications.size() == 0)) textviewNoAction.setVisibility(View.VISIBLE); else textviewNoAction.setVisibility(View.GONE); if( swiped ){ + Helper.cacheNotificationsClear(context,null); + ((MainActivity) context).updateNotifCounter(); boolean isOnWifi = Helper.isOnWIFI(context); int behaviorWithAttachments = sharedpreferences.getInt(Helper.SET_ATTACHMENT_ACTION, Helper.ATTACHMENT_ALWAYS); notificationsListAdapter = new NotificationsListAdapter(context,isOnWifi, behaviorWithAttachments, this.notifications); lv_notifications.setAdapter(notificationsListAdapter); swiped = false; } + ArrayList added = new ArrayList<>(); + for(Notification notification : this.notifications){ + added.add(notification.getId()); + } if( notifications != null && notifications.size() > 0) { for(Notification tmpNotification: notifications){ - this.notifications.add(tmpNotification); + if( !added.contains(tmpNotification.getId())) { + this.notifications.add(tmpNotification); + added.add(tmpNotification.getId()); + } } notificationsListAdapter.notifyDataSetChanged(); } @@ -284,52 +270,57 @@ public class DisplayNotificationsFragment extends Fragment implements OnRetrieve //Store last notification id to avoid to notify for those that have been already seen if( notifications != null && notifications.size() > 0) { //acct is null as userId when used in Fragment, data need to be retrieved via shared preferences and db - String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); + userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); SQLiteDatabase db = Sqlite.getInstance(context, Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); Account currentAccount = new AccountDAO(context, db).getAccountByID(userId); if( currentAccount != null && firstLoad && since_id != null){ - SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + currentAccount.getId(), since_id); + editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + currentAccount.getId(), notifications.get(0).getId()); editor.apply(); } } firstLoad = false; } + public void scrollToTop(){ if( lv_notifications != null) lv_notifications.setAdapter(notificationsListAdapter); } - public void update(){ - if( context != null){ - asyncTask = new RetrieveNotificationsAsyncTask(context, null, null, null, null, null, false, DisplayNotificationsFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + + public void showNewContent(){ + new_data.setVisibility(View.VISIBLE); + } + + public void refresh(){ + if( context == null) + return; + notificationsTmp = Helper.getTempNotification(context, null); + if( notificationsTmp.size() > 0){ + ArrayList added = new ArrayList<>(); + for(Notification notification : notifications){ + added.add(notification.getId()); + } + for(int i = notificationsTmp.size() -1 ; i >= 0 ; i--){ + if( !added.contains(notificationsTmp.get(i).getId())) { + this.notifications.add(0, notificationsTmp.get(i)); + added.add(notificationsTmp.get(i).getId()); + } + } + if( this.notifications.size() > 0 ) + max_id = this.notifications.get(this.notifications.size()-1).getId(); + boolean isOnWifi = Helper.isOnWIFI(context); + final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + int behaviorWithAttachments = sharedpreferences.getInt(Helper.SET_ATTACHMENT_ACTION, Helper.ATTACHMENT_ALWAYS); + SharedPreferences.Editor editor = sharedpreferences.edit(); + String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); + editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + userId, notificationsTmp.get(0).getId()); + editor.apply(); + notificationsListAdapter = new NotificationsListAdapter(context,isOnWifi, behaviorWithAttachments, notifications); + lv_notifications.setAdapter(notificationsListAdapter); + if( textviewNoAction.getVisibility() == View.VISIBLE) + textviewNoAction.setVisibility(View.GONE); } - } - - public void refreshData(){ - - final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); - - if(context != null && this.notificationsTmp != null && this.notificationsTmp.size() > 0){ - boolean isOnWifi = Helper.isOnWIFI(context); - int behaviorWithAttachments = sharedpreferences.getInt(Helper.SET_ATTACHMENT_ACTION, Helper.ATTACHMENT_ALWAYS); - notifications = new ArrayList<>(); - for(Notification notification: this.notificationsTmp){ - notifications.add(notification); - } - if( textviewNoAction.getVisibility() == View.VISIBLE) - textviewNoAction.setVisibility(View.GONE); - notificationsListAdapter = new NotificationsListAdapter(context,isOnWifi, behaviorWithAttachments, notifications); - lv_notifications.setAdapter(notificationsListAdapter); - this.notificationsTmp = new ArrayList<>(); - } - if( since_id != null){ - //The user clicked on the tab to refresh values so, the pointer is changed - SharedPreferences.Editor editor = sharedpreferences.edit(); - String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); - editor.putString(Helper.LAST_MAX_ID_BUBBLE_NOTIF + userId, since_id); - editor.apply(); - } - } + new_data.setVisibility(View.GONE); + } } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayStatusFragment.java b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayStatusFragment.java index e08356a50..ffab4fe3d 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayStatusFragment.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/DisplayStatusFragment.java @@ -25,6 +25,7 @@ import android.support.v4.app.Fragment; import android.support.v4.content.LocalBroadcastManager; import android.support.v4.view.ViewCompat; import android.support.v4.widget.SwipeRefreshLayout; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -75,20 +76,16 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn private boolean isOnWifi; private int behaviorWithAttachments; private boolean showMediaOnly; - private DisplayStatusFragment displayStatusFragment; private TextView new_data; private int positionSpinnerTrans; - private String since_id; private boolean hideHeader; private String instanceValue; public DisplayStatusFragment(){ - displayStatusFragment = this; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - View rootView = inflater.inflate(R.layout.fragment_status, container, false); statuses = new ArrayList<>(); context = getContext(); @@ -204,7 +201,6 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn R.color.mastodonC2, R.color.mastodonC3); - if( type == RetrieveFeedsAsyncTask.Type.USER) asyncTask = new RetrieveFeedsAsyncTask(context, type, targetedId, max_id, showMediaOnly, DisplayStatusFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); else if( type == RetrieveFeedsAsyncTask.Type.TAG) @@ -222,27 +218,29 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn new_data.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + statusesTmp = Helper.getTempStatus(context, null); if( statusesTmp != null){ + for(int i = statusesTmp.size() -1 ; i >= 0 ; i--){ + statuses.add(0,statusesTmp.get(i)); + } boolean isOnWifi = Helper.isOnWIFI(context); int behaviorWithAttachments = sharedpreferences.getInt(Helper.SET_ATTACHMENT_ACTION, Helper.ATTACHMENT_ALWAYS); - statuses = new ArrayList<>(); - for(Status status: statusesTmp){ - statuses.add(status); - } - //The user clicked on the banner to refresh values so, the pointer is changed - if( statusesTmp.size() > 0 ) { + statusListAdapter = new StatusListAdapter(context, type, targetedId, isOnWifi, behaviorWithAttachments, positionSpinnerTrans, statuses); + lv_status.setAdapter(statusListAdapter); + if( statusesTmp.size() > 0){ SharedPreferences.Editor editor = sharedpreferences.edit(); String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); - editor.putString(Helper.LAST_MAX_ID_BUBBLE_HOME + userId, statusesTmp.get(0).getId()); + editor.putString(Helper.LAST_HOMETIMELINE_MAX_ID + userId, statusesTmp.get(0).getId()); editor.apply(); } if( statusesTmp.size() > 0 && textviewNoAction.getVisibility() == View.VISIBLE) textviewNoAction.setVisibility(View.GONE); - statusListAdapter = new StatusListAdapter(context, type, targetedId, isOnWifi, behaviorWithAttachments, positionSpinnerTrans, statuses); - lv_status.setAdapter(statusListAdapter); - statusesTmp = new ArrayList<>(); } new_data.setVisibility(View.GONE); + statusesTmp = new ArrayList<>(); + Helper.cacheStatusClear(context, null); + ((MainActivity) context).updateHomeCounter(); + } }); @@ -261,24 +259,29 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn @Override public void onResume() { super.onResume(); - //New data are available - if (getUserVisibleHint() && statusesTmp != null && statusesTmp.size() > 0 ) { - final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); - boolean isOnWifi = Helper.isOnWIFI(context); - int behaviorWithAttachments = sharedpreferences.getInt(Helper.SET_ATTACHMENT_ACTION, Helper.ATTACHMENT_ALWAYS); - int positionSpinnerTrans = sharedpreferences.getInt(Helper.SET_TRANSLATOR, Helper.TRANS_YANDEX); - statuses = new ArrayList<>(); - for(Status status: statusesTmp){ - statuses.add(status); + if( type == RetrieveFeedsAsyncTask.Type.HOME ) { + //New data are available + statusesTmp = Helper.getTempStatus(context, null); + if (getUserVisibleHint() && statusesTmp != null && statusesTmp.size() > 0 && statuses.size() > 0) { + ArrayList added = new ArrayList<>(); + for (Status status : statuses) { + added.add(status.getId()); + } + final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + boolean isOnWifi = Helper.isOnWIFI(context); + int behaviorWithAttachments = sharedpreferences.getInt(Helper.SET_ATTACHMENT_ACTION, Helper.ATTACHMENT_ALWAYS); + int positionSpinnerTrans = sharedpreferences.getInt(Helper.SET_TRANSLATOR, Helper.TRANS_YANDEX); + for (int i = statusesTmp.size() - 1; i >= 0; i--) { + if (!added.contains(statusesTmp.get(i).getId())) { + this.statuses.add(0, statusesTmp.get(i)); + added.add(statusesTmp.get(i).getId()); + } + } + if (this.statuses.size() > 0) + max_id = this.statuses.get(this.statuses.size() - 1).getId(); + statusListAdapter = new StatusListAdapter(context, type, targetedId, isOnWifi, behaviorWithAttachments, positionSpinnerTrans, statuses); + lv_status.setAdapter(statusListAdapter); } - //The user clicked on the tab to refresh values so, the pointer is changed - SharedPreferences.Editor editor = sharedpreferences.edit(); - String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); - editor.putString(Helper.LAST_MAX_ID_BUBBLE_HOME + userId, statusesTmp.get(0).getId()); - editor.apply(); - statusListAdapter = new StatusListAdapter(context, type, targetedId, isOnWifi, behaviorWithAttachments, positionSpinnerTrans, statuses); - lv_status.setAdapter(statusListAdapter); - statusesTmp = new ArrayList<>(); } } @@ -313,87 +316,33 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn return; } List statuses = apiResponse.getStatuses(); - since_id = apiResponse.getSince_id(); + String since_id = apiResponse.getSince_id(); max_id = apiResponse.getMax_id(); - //Special case for home timeline - if( type == RetrieveFeedsAsyncTask.Type.HOME){ - //Retrieves some values - SharedPreferences.Editor editor = sharedpreferences.edit(); - String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); - String bubble_max_id = sharedpreferences.getString(Helper.LAST_MAX_ID_BUBBLE_HOME + userId, null); - //The initial call comes from a classic tab refresh - if( refreshData ) { - - manageStatus(statuses, max_id, since_id); - //The current tab is displayed, so user is supposed to have seen status - if( since_id != null && displayStatusFragment.getUserVisibleHint()) { - editor.putString(Helper.LAST_MAX_ID_BUBBLE_HOME + userId, since_id); - editor.apply(); - }else if(!displayStatusFragment.getUserVisibleHint()){ - //Current fragment was loaded but not displayed to the user. - //So the bubble counter will be displayed - int countData = 0; - //Retrieves new status count - if( bubble_max_id != null) { - for (Status st : statuses) { - if (st.getId().trim().equals(bubble_max_id.trim())) - break; - countData++; - } - } - ((MainActivity)context).updateHomeCounter(countData); - } - }else { //Here, new values have been retrieved on the onResume call (forced mode) - int countData = 0; - if( bubble_max_id != null) { - for (Status st : statuses) { - if (st.getId().trim().equals(bubble_max_id.trim())) - break; - countData++; - } - } - - if( statuses != null && statuses.size() > 0 && countData > 0) { - max_id = null; - firstLoad = true; - statusesTmp = new ArrayList<>(); - for (Status tmpStatus : statuses) { - this.statusesTmp.add(tmpStatus); - } - //New status will be counted - //The fragment is not displayed, so the bubble counter should be shown - if (!displayStatusFragment.getUserVisibleHint()) { - ((MainActivity) context).updateHomeCounter(countData); - } else { - //The current fragment is visible, but for avoiding to populate with new values - //Values are put in temp and the banned is displayed - new_data.setVisibility(View.VISIBLE); - } - } - } - }else { - manageStatus(statuses, max_id, since_id); - } - - - - } - - private void manageStatus(List statuses, String max_id, String since_id){ flag_loading = (max_id == null ); if( !swiped && firstLoad && (statuses == null || statuses.size() == 0)) textviewNoAction.setVisibility(View.VISIBLE); else textviewNoAction.setVisibility(View.GONE); if( swiped ){ + if( type == RetrieveFeedsAsyncTask.Type.HOME ) { + Helper.cacheStatusClear(context,null); + ((MainActivity) context).updateHomeCounter(); + } statusListAdapter = new StatusListAdapter(context, type, targetedId, isOnWifi, behaviorWithAttachments, positionSpinnerTrans, this.statuses); lv_status.setAdapter(statusListAdapter); swiped = false; } + ArrayList added = new ArrayList<>(); + for(Status status : this.statuses){ + added.add(status.getId()); + } if( statuses != null && statuses.size() > 0) { for(Status tmpStatus: statuses){ - this.statuses.add(tmpStatus); + if( !added.contains(tmpStatus.getId())) { + this.statuses.add(tmpStatus); + added.add(tmpStatus.getId()); + } } statusListAdapter.notifyDataSetChanged(); } @@ -401,14 +350,13 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn //Store last toot id for home timeline to avoid to notify for those that have been already seen if(statuses != null && statuses.size() > 0 && type == RetrieveFeedsAsyncTask.Type.HOME ){ - final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); //acct is null when used in Fragment, data need to be retrieved via shared preferences and db String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); SQLiteDatabase db = Sqlite.getInstance(context, Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); Account currentAccount = new AccountDAO(context, db).getAccountByID(userId); if( currentAccount != null && firstLoad && since_id != null){ SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.LAST_HOMETIMELINE_MAX_ID + currentAccount.getId(), since_id); + editor.putString(Helper.LAST_HOMETIMELINE_MAX_ID + currentAccount.getId(), statuses.get(0).getId()); editor.apply(); } } @@ -416,8 +364,7 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn //Retrieves replies if(statuses != null && statuses.size() > 0 && type == RetrieveFeedsAsyncTask.Type.HOME ) { - final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); - boolean showPreview = sharedpreferences.getBoolean(Helper.SET_PREVIEW_REPLIES, true); + boolean showPreview = sharedpreferences.getBoolean(Helper.SET_PREVIEW_REPLIES, false); //Retrieves attached replies to a toot if (showPreview) { new RetrieveRepliesAsyncTask(context, statuses, DisplayStatusFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); @@ -425,6 +372,52 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn } } + public void showNewContent(){ + new_data.setVisibility(View.VISIBLE); + } + + @Override + public void setUserVisibleHint(boolean isVisibleToUser) { + super.setUserVisibleHint(isVisibleToUser); + if( isVisibleToUser ) + refresh(); + } + + public void refresh(){ + //New data are available + if( type == RetrieveFeedsAsyncTask.Type.HOME ) { + if (context == null) + return; + statusesTmp = Helper.getTempStatus(context, null); + if (statusesTmp.size() > 0) { + ArrayList added = new ArrayList<>(); + for (Status status : statuses) { + added.add(status.getId()); + } + for (int i = statusesTmp.size() - 1; i >= 0; i--) { + if (!added.contains(statusesTmp.get(i).getId())) { + this.statuses.add(0, statusesTmp.get(i)); + added.add(statusesTmp.get(i).getId()); + } + } + if (this.statuses.size() > 0) + max_id = this.statuses.get(this.statuses.size() - 1).getId(); + boolean isOnWifi = Helper.isOnWIFI(context); + final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + int behaviorWithAttachments = sharedpreferences.getInt(Helper.SET_ATTACHMENT_ACTION, Helper.ATTACHMENT_ALWAYS); + SharedPreferences.Editor editor = sharedpreferences.edit(); + String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); + editor.putString(Helper.LAST_HOMETIMELINE_MAX_ID + userId, statusesTmp.get(0).getId()); + editor.apply(); + statusListAdapter = new StatusListAdapter(context, type, targetedId, isOnWifi, behaviorWithAttachments, positionSpinnerTrans, statuses); + lv_status.setAdapter(statusListAdapter); + if (textviewNoAction.getVisibility() == View.VISIBLE) + textviewNoAction.setVisibility(View.GONE); + } + new_data.setVisibility(View.GONE); + } + } + public void scrollToTop(){ if( lv_status != null) lv_status.setAdapter(statusListAdapter); @@ -444,36 +437,4 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn } statusListAdapter.notifyDataSetChanged(); } - public void update() { - if( context != null) { - asyncTask = new RetrieveFeedsAsyncTask(context, type, null, false, DisplayStatusFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } - } - - public void refreshData(){ - final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); - if(context != null && this.statusesTmp != null && this.statusesTmp.size() > 0){ - - boolean isOnWifi = Helper.isOnWIFI(context); - int behaviorWithAttachments = sharedpreferences.getInt(Helper.SET_ATTACHMENT_ACTION, Helper.ATTACHMENT_ALWAYS); - int positionSpinnerTrans = sharedpreferences.getInt(Helper.SET_TRANSLATOR, Helper.TRANS_YANDEX); - - statuses = new ArrayList<>(); - for(Status status: statusesTmp){ - statuses.add(status); - } - if( textviewNoAction.getVisibility() == View.VISIBLE) - textviewNoAction.setVisibility(View.GONE); - statusListAdapter = new StatusListAdapter(context, type, targetedId, isOnWifi, behaviorWithAttachments, positionSpinnerTrans, statuses); - lv_status.setAdapter(statusListAdapter); - statusesTmp = new ArrayList<>(); - } - if( since_id != null){ - //The user clicked on the tab to refresh values so, the pointer is changed - SharedPreferences.Editor editor = sharedpreferences.edit(); - String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); - editor.putString(Helper.LAST_MAX_ID_BUBBLE_HOME + userId, since_id); - editor.apply(); - } - } } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/SettingsFragment.java b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/SettingsFragment.java index 2783602f9..95f2e93a5 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/SettingsFragment.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/SettingsFragment.java @@ -94,19 +94,6 @@ public class SettingsFragment extends Fragment { } }); - boolean bubble_counter = sharedpreferences.getBoolean(Helper.SET_BUBBLE_COUNTER, true); - - final CheckBox set_bubble_counter = (CheckBox) rootView.findViewById(R.id.set_bubble_counter); - set_bubble_counter.setChecked(bubble_counter); - set_bubble_counter.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putBoolean(Helper.SET_BUBBLE_COUNTER, set_bubble_counter.isChecked()); - editor.apply(); - } - }); - boolean show_error_messages = sharedpreferences.getBoolean(Helper.SET_SHOW_ERROR_MESSAGES, true); final CheckBox set_show_error_messages = (CheckBox) rootView.findViewById(R.id.set_show_error_messages); set_show_error_messages.setChecked(show_error_messages); @@ -133,7 +120,7 @@ public class SettingsFragment extends Fragment { } }); - boolean preview_reply = sharedpreferences.getBoolean(Helper.SET_PREVIEW_REPLIES, true); + boolean preview_reply = sharedpreferences.getBoolean(Helper.SET_PREVIEW_REPLIES, false); final CheckBox set_preview_reply = (CheckBox) rootView.findViewById(R.id.set_preview_reply); final LinearLayout set_preview_reply_pp_container = (LinearLayout) rootView.findViewById(R.id.set_preview_reply_pp_container); final SwitchCompat set_preview_reply_pp = (SwitchCompat) rootView.findViewById(R.id.set_preview_reply_pp); @@ -152,6 +139,7 @@ public class SettingsFragment extends Fragment { } } }); + if( !preview_reply){ set_preview_reply_pp_container.setVisibility(View.GONE); }else{ @@ -181,6 +169,53 @@ public class SettingsFragment extends Fragment { } }); + boolean notif_validation_fav = sharedpreferences.getBoolean(Helper.SET_NOTIF_VALIDATION_FAV, false); + final CheckBox set_share_validation_fav = (CheckBox) rootView.findViewById(R.id.set_share_validation_fav); + set_share_validation_fav.setChecked(notif_validation_fav); + + set_share_validation_fav.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + SharedPreferences.Editor editor = sharedpreferences.edit(); + editor.putBoolean(Helper.SET_NOTIF_VALIDATION_FAV, set_share_validation_fav.isChecked()); + editor.apply(); + } + }); + + boolean display_local = sharedpreferences.getBoolean(Helper.SET_DISPLAY_LOCAL, true); + final CheckBox set_display_local = (CheckBox) rootView.findViewById(R.id.set_display_local); + set_display_local.setChecked(display_local); + + set_display_local.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + SharedPreferences.Editor editor = sharedpreferences.edit(); + editor.putBoolean(Helper.SET_DISPLAY_LOCAL, set_display_local.isChecked()); + editor.apply(); + getActivity().recreate(); + Intent intent = new Intent(context, MainActivity.class); + intent.putExtra(INTENT_ACTION, CHANGE_THEME_INTENT); + startActivity(intent); + } + }); + + + boolean display_global = sharedpreferences.getBoolean(Helper.SET_DISPLAY_GLOBAL, true); + final CheckBox set_display_global = (CheckBox) rootView.findViewById(R.id.set_display_global); + set_display_global.setChecked(display_global); + + set_display_global.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + SharedPreferences.Editor editor = sharedpreferences.edit(); + editor.putBoolean(Helper.SET_DISPLAY_GLOBAL, set_display_global.isChecked()); + editor.apply(); + getActivity().recreate(); + Intent intent = new Intent(context, MainActivity.class); + intent.putExtra(INTENT_ACTION, CHANGE_THEME_INTENT); + startActivity(intent); + } + }); final CheckBox set_embedded_browser = (CheckBox) rootView.findViewById(R.id.set_embedded_browser); final LinearLayout set_javascript_container = (LinearLayout) rootView.findViewById(R.id.set_javascript_container); diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/SettingsNotificationsFragment.java b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/SettingsNotificationsFragment.java index a9abbeebd..098995f91 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/SettingsNotificationsFragment.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/SettingsNotificationsFragment.java @@ -26,10 +26,14 @@ import android.support.v7.widget.SwitchCompat; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.CheckBox; import android.widget.CompoundButton; +import android.widget.Spinner; +import android.widget.TextView; import android.widget.TimePicker; import android.widget.Toast; @@ -49,6 +53,9 @@ public class SettingsNotificationsFragment extends Fragment { private Context context; private int style; + + int count = 0; + @Override public View onCreateView(LayoutInflater inflater, final ViewGroup container, Bundle savedInstanceState) { @@ -217,6 +224,10 @@ public class SettingsNotificationsFragment extends Fragment { } }); + + final Spinner led_colour_spinner = (Spinner) rootView.findViewById(R.id.led_colour_spinner); + final TextView ledLabel = (TextView) rootView.findViewById(R.id.set_led_colour_label); + switchCompatSilent.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { @@ -224,8 +235,55 @@ public class SettingsNotificationsFragment extends Fragment { SharedPreferences.Editor editor = sharedpreferences.edit(); editor.putBoolean(Helper.SET_NOTIF_SILENT, isChecked); editor.apply(); + + if (isChecked) { + ledLabel.setEnabled(true); + led_colour_spinner.setEnabled(true); + } else { + ledLabel.setEnabled(false); + for (View lol : led_colour_spinner.getTouchables()) { + lol.setEnabled(false); + } + } } }); + + if (sharedpreferences.getBoolean(Helper.SET_NOTIF_SILENT, false)) { + + ledLabel.setEnabled(true); + led_colour_spinner.setEnabled(true); + + ArrayAdapter adapterLEDColour = ArrayAdapter.createFromResource(getActivity(), + R.array.led_colours, android.R.layout.simple_spinner_item); + led_colour_spinner.setAdapter(adapterLEDColour); + int positionSpinnerLEDColour = (sharedpreferences.getInt(Helper.SET_LED_COLOUR, Helper.LED_COLOUR)); + led_colour_spinner.setSelection(positionSpinnerLEDColour); + + led_colour_spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + if (count > 0) { + SharedPreferences.Editor editor = sharedpreferences.edit(); + editor.putInt(Helper.SET_LED_COLOUR, position); + editor.apply(); + } else { + count++; + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + } + }); + } + + else { + ledLabel.setEnabled(false); + for (View lol : led_colour_spinner.getTouchables()) { + lol.setEnabled(false); + } + } + if( theme == Helper.THEME_LIGHT) { settings_time_from.setTextColor(ContextCompat.getColor(context, R.color.white)); settings_time_to.setTextColor(ContextCompat.getColor(context, R.color.white)); diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/TabLayoutSettingsFragment.java b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/TabLayoutSettingsFragment.java index 687b5625f..654c58540 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/fragments/TabLayoutSettingsFragment.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/fragments/TabLayoutSettingsFragment.java @@ -43,7 +43,6 @@ public class TabLayoutSettingsFragment extends Fragment { tabLayout.addTab(tabLayout.newTab().setText(getString(R.string.settings))); tabLayout.addTab(tabLayout.newTab().setText(getString(R.string.notifications))); tabLayout.addTab(tabLayout.newTab().setText(getString(R.string.optimization))); - tabLayout.addTab(tabLayout.newTab().setText(getString(R.string.profile))); final ViewPager viewPager = (ViewPager) inflatedView.findViewById(R.id.viewpager); viewPager.setAdapter(new PagerAdapter @@ -90,8 +89,6 @@ public class TabLayoutSettingsFragment extends Fragment { return new SettingsNotificationsFragment(); case 2: return new SettingsOptimizationFragment(); - case 3: - return new SettingsProfileFragment(); default: return new SettingsNotificationsFragment(); } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/helper/ExpandableHeightListView.java b/app/src/main/java/fr/gouv/etalab/mastodon/helper/ExpandableHeightListView.java new file mode 100644 index 000000000..bf207a0f8 --- /dev/null +++ b/app/src/main/java/fr/gouv/etalab/mastodon/helper/ExpandableHeightListView.java @@ -0,0 +1,64 @@ +package fr.gouv.etalab.mastodon.helper; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ListView; + +/** + * Created by Thomas on 03/09/2017. + * Expanded listview + */ + +public class ExpandableHeightListView extends ListView + { + + boolean expanded = false; + + public ExpandableHeightListView(Context context) + { + super(context); + } + + public ExpandableHeightListView(Context context, AttributeSet attrs) + { + super(context, attrs); + } + + public ExpandableHeightListView(Context context, AttributeSet attrs,int defStyle) + { + super(context, attrs, defStyle); + } + + public boolean isExpanded() + { + return expanded; + } + + @Override + public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) + { + // HACK! TAKE THAT ANDROID! + if (isExpanded()) + { + // Calculate entire height by providing a very large height hint. + // But do not use the highest 2 bits of this integer; those are + // reserved for the MeasureSpec mode. + int expandSpec = View.MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, View.MeasureSpec.AT_MOST); + super.onMeasure(widthMeasureSpec, expandSpec); + + ViewGroup.LayoutParams params = getLayoutParams(); + params.height = getMeasuredHeight(); + } + else + { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + } + } + + public void setExpanded(boolean expanded) + { + this.expanded = expanded; + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/helper/Helper.java b/app/src/main/java/fr/gouv/etalab/mastodon/helper/Helper.java index 1e71beb5c..7f684d329 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/helper/Helper.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/helper/Helper.java @@ -18,6 +18,8 @@ package fr.gouv.etalab.mastodon.helper; import android.app.Activity; +import android.app.NotificationManager; +import android.preference.PreferenceManager; import android.support.annotation.RequiresApi; import android.support.v7.app.AlertDialog; import android.app.DownloadManager; @@ -81,6 +83,7 @@ import android.widget.TextView; import android.widget.Toast; import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; import com.loopj.android.http.BuildConfig; import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache; import com.nostra13.universalimageloader.core.DisplayImageOptions; @@ -92,19 +95,23 @@ import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener; import java.io.BufferedReader; +import java.io.BufferedWriter; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; +import java.lang.reflect.Type; import java.net.InetAddress; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.HashMap; @@ -116,6 +123,7 @@ import java.util.TimeZone; import java.util.regex.Matcher; import java.util.regex.Pattern; +import fr.gouv.etalab.mastodon.activities.EditProfileActivity; import fr.gouv.etalab.mastodon.activities.HashTagActivity; import fr.gouv.etalab.mastodon.activities.LoginActivity; import fr.gouv.etalab.mastodon.activities.MainActivity; @@ -126,14 +134,13 @@ import fr.gouv.etalab.mastodon.asynctasks.RemoveAccountAsyncTask; import fr.gouv.etalab.mastodon.client.API; import fr.gouv.etalab.mastodon.client.Entities.Account; import fr.gouv.etalab.mastodon.client.Entities.Mention; +import fr.gouv.etalab.mastodon.client.Entities.Notification; import fr.gouv.etalab.mastodon.client.Entities.Status; import fr.gouv.etalab.mastodon.client.PatchBaseImageDownloader; import fr.gouv.etalab.mastodon.sqlite.AccountDAO; import fr.gouv.etalab.mastodon.sqlite.Sqlite; import mastodon.etalab.gouv.fr.mastodon.R; -import static android.app.Notification.DEFAULT_VIBRATE; -import static android.app.Notification.FLAG_SHOW_LIGHTS; import static android.content.Context.DOWNLOAD_SERVICE; @@ -177,10 +184,6 @@ public class Helper { public static final String SHOW_BATTERY_SAVER_MESSAGE = "show_battery_saver_message"; public static final String LAST_NOTIFICATION_MAX_ID = "last_notification_max_id"; public static final String LAST_HOMETIMELINE_MAX_ID = "last_hometimeline_max_id"; - public static final String LAST_BUBBLE_REFRESH_NOTIF = "last_bubble_refresh_notif"; - public static final String LAST_BUBBLE_REFRESH_HOME = "last_bubble_refresh_home"; - public static final String LAST_MAX_ID_BUBBLE_NOTIF = "last_max_id_bubble_notif"; - public static final String LAST_MAX_ID_BUBBLE_HOME = "last_max_id_bubble_home"; public static final String CLIP_BOARD = "clipboard"; //Notifications public static final int NOTIFICATION_INTENT = 1; @@ -204,8 +207,10 @@ public class Helper { public static final String SET_ICON_SIZE = "set_icon_size"; public static final String SET_PREVIEW_REPLIES = "set_preview_replies"; public static final String SET_PREVIEW_REPLIES_PP = "set_preview_replies_pp"; - public static final String SET_BUBBLE_COUNTER = "set_bubble_counter"; public static final String SET_TRANSLATOR = "set_translator"; + public static final String SET_LED_COLOUR = "set_led_colour"; + private static final String SET_TEMP_STATUS = "set_temp_status"; + private static final String SET_TEMP_NOTIFICATIONS = "set_temp_notifications"; public static final int ATTACHMENT_ALWAYS = 1; public static final int ATTACHMENT_WIFI = 2; @@ -216,6 +221,8 @@ public class Helper { public static final int THEME_MENU = 2; public static final int THEME_MENU_TABS = 3; + public static final int LED_COLOUR = 0; + public static final int TRANS_YANDEX = 0; public static final int TRANS_GOOGLE = 1; public static final int TRANS_NONE = 2; @@ -226,6 +233,7 @@ public class Helper { public static final String SET_NOTIF_MENTION = "set_notif_follow_mention"; public static final String SET_NOTIF_SHARE = "set_notif_follow_share"; public static final String SET_NOTIF_VALIDATION = "set_share_validation"; + public static final String SET_NOTIF_VALIDATION_FAV = "set_share_validation_fav"; public static final String SET_WIFI_ONLY = "set_wifi_only"; public static final String SET_NOTIF_HOMETIMELINE = "set_notif_hometimeline"; public static final String SET_NOTIF_SILENT = "set_notif_silent"; @@ -235,6 +243,8 @@ public class Helper { public static final String SET_COOKIES = "set_cookies"; public static final String SET_FOLDER_RECORD = "set_folder_record"; public static final String SET_TOOT_VISIBILITY = "set_toot_visibility"; + public static final String SET_DISPLAY_LOCAL = "set_display_local"; + public static final String SET_DISPLAY_GLOBAL = "set_display_global"; //End points public static final String EP_AUTHORIZE = "/oauth/authorize"; @@ -250,7 +260,8 @@ public class Helper { //Receiver public static final String SEARCH_VALIDATE_ACCOUNT = "search_validate_account"; public static final String HEADER_ACCOUNT = "header_account"; - + public static final String RECEIVE_DATA = "receive_data"; + public static final String RECEIVE_PICTURE = "receive_picture"; //User agent public static final String USER_AGENT = "Mastalab/"+ BuildConfig.VERSION_NAME + " Android/"+ Build.VERSION.RELEASE; @@ -258,7 +269,7 @@ public class Helper { private static boolean menuAccountsOpened = false; - private static final Pattern SHORTNAME_PATTERN = Pattern.compile(":([-+\\w]+):"); + private static final Pattern SHORTNAME_PATTERN = Pattern.compile(":( |)([-+\\w]+):"); public static final Pattern urlPattern = Pattern.compile( "(?i)\\b((?:[a-z][\\w-]+:(?:/{1,3}|[a-z0-9%])|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,10}/)(?:[^\\s()<>]+|\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\))+(?:\\(([^\\s()<>]+|(\\([^\\s()<>]+\\)))*\\)|[^\\s`!()\\[\\]{};:'\".,<>?«»“”‘’]))", @@ -273,18 +284,26 @@ public class Helper { */ public static String shortnameToUnicode(String input, boolean removeIfUnsupported) { Matcher matcher = SHORTNAME_PATTERN.matcher(input); + boolean supported = Build.VERSION.SDK_INT >= 16; while (matcher.find()) { - String unicode = emoji.get(matcher.group(1)); + String unicode = emoji.get(matcher.group(2)); if (unicode == null) { continue; } if (supported) { - input = input.replace(":" + matcher.group(1) + ":", unicode); + if (matcher.group(1).equals(" ")) + input = input.replace(": " + matcher.group(2) + ":", unicode); + else + input = input.replace(":" + matcher.group(2) + ":", unicode); } else if (removeIfUnsupported) { - input = input.replace(":" + matcher.group(1) + ":", ""); + if (matcher.group(1).equals(" ")) + input = input.replace(": " + matcher.group(2) + ":", unicode); + else + input = input.replace(":" + matcher.group(2) + ":", ""); } } + return input; } //Emoji manager @@ -596,16 +615,40 @@ public class Helper { .setAutoCancel(true) .setContentIntent(pIntent) .setContentText(message); - - int notifDefaults = FLAG_SHOW_LIGHTS; - notificationBuilder.setDefaults(notifDefaults); if( sharedpreferences.getBoolean(Helper.SET_NOTIF_SILENT,false) ) { - notificationBuilder.setDefaults(notifDefaults|DEFAULT_VIBRATE); + notificationBuilder.setVibrate(new long[] { 500, 500, 500}); }else { String soundUri = ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + context.getPackageName() +"/"; notificationBuilder.setSound(Uri.parse(soundUri + R.raw.boop)); } - notificationBuilder.setLights(Color.BLUE, 500, 1000); + + int ledColour = Color.BLUE; + + switch (sharedpreferences.getInt(Helper.SET_LED_COLOUR, Helper.LED_COLOUR)) { + case 0: // BLUE + ledColour = Color.BLUE; + break; + case 1: // CYAN + ledColour = Color.CYAN; + break; + case 2: // MAGENTA + ledColour = Color.MAGENTA; + break; + case 3: // GREEN + ledColour = Color.GREEN; + break; + case 4: // RED + ledColour = Color.RED; + break; + case 5: // YELLOW + ledColour = Color.YELLOW; + break; + case 6: // WHITE + ledColour = Color.WHITE; + break; + } + + notificationBuilder.setLights(ledColour, 500, 1000); notificationBuilder.setContentTitle(title); notificationBuilder.setLargeIcon(icon); notificationManager.notify(notificationId, notificationBuilder.build()); @@ -964,6 +1007,8 @@ public class Helper { TextView ownerStatus = (TextView) headerLayout.findViewById(R.id.owner_status); TextView ownerFollowing = (TextView) headerLayout.findViewById(R.id.owner_following); TextView ownerFollowers = (TextView) headerLayout.findViewById(R.id.owner_followers); + ImageView header_edit_profile = (ImageView) headerLayout.findViewById(R.id.header_edit_profile); + header_edit_profile.setOnClickListener(null); if( account == null ) { Helper.logout(activity); Intent myIntent = new Intent(activity, LoginActivity.class); @@ -1013,6 +1058,13 @@ public class Helper { } }); } + header_edit_profile.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(activity, EditProfileActivity.class); + activity.startActivity(intent); + } + }); } profilePicture.setOnClickListener(null); profilePicture.setOnClickListener(new View.OnClickListener() { @@ -1544,4 +1596,124 @@ public class Helper { } } } + + + public static int getUnreadNotifications(Context context, String userId){ + SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + if( userId == null) + userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); + Gson gson = new Gson(); + String json = sharedpreferences.getString(Helper.SET_TEMP_NOTIFICATIONS + userId, null); + Type type = new TypeToken>() {}.getType(); + ArrayList notifications = gson.fromJson(json, type); + return (notifications == null)?0:notifications.size(); + } + + + + public static int getUnreadToots(Context context, String userId){ + SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + if( userId == null) + userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); + Gson gson = new Gson(); + String json = sharedpreferences.getString(Helper.SET_TEMP_STATUS + userId, null); + Type type = new TypeToken>() {}.getType(); + ArrayList statuses = gson.fromJson(json, type); + return (statuses == null)?0:statuses.size(); + } + + + public static void cacheStatus(Context context, Status status, String userId){ + SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + if( userId == null) + userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); + SharedPreferences.Editor editor = sharedpreferences.edit(); + ArrayList statuses = getTempStatus(context, userId); + if( statuses == null) + statuses = new ArrayList<>(); + if( status != null) + statuses.add(0,status); + Gson gson = new Gson(); + String serializedAccounts = gson.toJson(statuses); + editor.putString(Helper.SET_TEMP_STATUS + userId, serializedAccounts); + editor.apply(); + } + + public static void cacheStatusClear(Context context, String userId){ + SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + if( userId == null) + userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); + SharedPreferences.Editor editor = sharedpreferences.edit(); + ArrayList statuses = new ArrayList<>(); + Gson gson = new Gson(); + String serializedAccounts = gson.toJson(statuses); + editor.putString(Helper.SET_TEMP_STATUS + userId, serializedAccounts); + editor.apply(); + //noinspection EmptyTryBlock + try { + NotificationManager notificationManager = (NotificationManager) context.getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE); + long notif_id = Long.parseLong(userId); + int notificationId = ((notif_id + 2) > 2147483647) ? (int) (2147483647 - notif_id - 2) : (int) (notif_id + 2); + notificationManager.cancel(notificationId); + }catch (Exception ignored){} + } + + public static ArrayList getTempStatus(Context context, String userId){ + SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + if( userId == null) + userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); + Gson gson = new Gson(); + String json = sharedpreferences.getString(Helper.SET_TEMP_STATUS + userId, null); + Type type = new TypeToken>() {}.getType(); + return gson.fromJson(json, type); + } + + + public static void cacheNotifications(Context context, Notification notification, String userId){ + SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + if( userId == null) + userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); + SharedPreferences.Editor editor = sharedpreferences.edit(); + ArrayList notifications = getTempNotification(context, userId); + if( notifications == null) + notifications = new ArrayList<>(); + if( notification != null) + notifications.add(0,notification); + Gson gson = new Gson(); + String serializedAccounts = gson.toJson(notifications); + editor.putString(Helper.SET_TEMP_NOTIFICATIONS + userId, serializedAccounts); + editor.apply(); + } + + public static void cacheNotificationsClear(Context context, String userId){ + + SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + if( userId == null) + userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); + SharedPreferences.Editor editor = sharedpreferences.edit(); + ArrayList notifications = new ArrayList<>(); + Gson gson = new Gson(); + String serializedAccounts = gson.toJson(notifications); + editor.putString(Helper.SET_TEMP_NOTIFICATIONS + userId, serializedAccounts); + editor.apply(); + //noinspection EmptyTryBlock + try { + NotificationManager notificationManager = (NotificationManager) context.getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE); + long notif_id = Long.parseLong(userId); + int notificationId = ((notif_id + 1) > 2147483647) ? (int) (2147483647 - notif_id - 1) : (int) (notif_id + 1); + notificationManager.cancel(notificationId); + }catch (Exception ignored){} + + } + + public static ArrayList getTempNotification(Context context, String userId){ + SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + if( userId == null) + userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); + Gson gson = new Gson(); + String json = sharedpreferences.getString(Helper.SET_TEMP_NOTIFICATIONS + userId, null); + Type type = new TypeToken>() {}.getType(); + return gson.fromJson(json, type); + } + } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/helper/ManageHeader.java b/app/src/main/java/fr/gouv/etalab/mastodon/helper/ManageHeader.java new file mode 100644 index 000000000..cca3a5574 --- /dev/null +++ b/app/src/main/java/fr/gouv/etalab/mastodon/helper/ManageHeader.java @@ -0,0 +1,9 @@ +package fr.gouv.etalab.mastodon.helper; + +/** + * Created by Thomas on 02/09/2017. + */ + +public class ManageHeader { + +} diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/interfaces/OnRetrieveMetaDataInterface.java b/app/src/main/java/fr/gouv/etalab/mastodon/interfaces/OnRetrieveMetaDataInterface.java new file mode 100644 index 000000000..c42aec9ac --- /dev/null +++ b/app/src/main/java/fr/gouv/etalab/mastodon/interfaces/OnRetrieveMetaDataInterface.java @@ -0,0 +1,24 @@ +/* Copyright 2017 Thomas Schneider + * + * This file is a part of Mastalab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Mastalab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Mastalab; if not, + * see . */ +package fr.gouv.etalab.mastodon.interfaces; + + +/** + * Created by Thomas on 02/09/2017. + * Interface for retrieving meta data + */ +public interface OnRetrieveMetaDataInterface { + void onRetrieveMetaData(boolean error, String image, String title, String description); +} diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/interfaces/OnRetrieveRemoteAccountInterface.java b/app/src/main/java/fr/gouv/etalab/mastodon/interfaces/OnRetrieveRemoteAccountInterface.java index 4d984933e..200e7f3e0 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/interfaces/OnRetrieveRemoteAccountInterface.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/interfaces/OnRetrieveRemoteAccountInterface.java @@ -20,5 +20,5 @@ package fr.gouv.etalab.mastodon.interfaces; * Interface for retrieving a remote account */ public interface OnRetrieveRemoteAccountInterface { - void onRetrieveRemoteAccount(boolean error, String name, String username, boolean locked, String avatar, String bio, int statusCount, int followingCount, int followersCount); + void onRetrieveRemoteAccount(boolean error, String name, String username, String instance, boolean locked, String avatar, String bio, String statusCount, String followingCount, String followersCount); } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/interfaces/OnRetrieveSearchDevelopersAccountshInterface.java b/app/src/main/java/fr/gouv/etalab/mastodon/interfaces/OnRetrieveSearchDevelopersAccountshInterface.java new file mode 100644 index 000000000..12b1db865 --- /dev/null +++ b/app/src/main/java/fr/gouv/etalab/mastodon/interfaces/OnRetrieveSearchDevelopersAccountshInterface.java @@ -0,0 +1,28 @@ +/* Copyright 2017 Thomas Schneider + * + * This file is a part of Mastalab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Mastalab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Mastalab; if not, + * see . */ +package fr.gouv.etalab.mastodon.interfaces; + +import java.util.ArrayList; + +import fr.gouv.etalab.mastodon.client.Entities.Account; + + +/** + * Created by Thomas on 03/09/2017. + * Interface for search dev accounts + */ +public interface OnRetrieveSearchDevelopersAccountshInterface { + void onRetrieveSearchDevelopersAccounts(ArrayList accounts); +} diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/jobs/HomeTimelineSyncJob.java b/app/src/main/java/fr/gouv/etalab/mastodon/jobs/HomeTimelineSyncJob.java index 19cd2d843..59b29caa1 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/jobs/HomeTimelineSyncJob.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/jobs/HomeTimelineSyncJob.java @@ -127,24 +127,17 @@ public class HomeTimelineSyncJob extends Job implements OnRetrieveHomeTimelineSe @Override - public void onRetrieveHomeTimelineService(APIResponse apiResponse, String acct, String userId) { - List statuses = apiResponse.getStatuses(); + public void onRetrieveHomeTimelineService(APIResponse apiResponse, String acct, final String userId) { + final List statuses = apiResponse.getStatuses(); if( apiResponse.getError() != null || statuses == null || statuses.size() == 0) return; final SharedPreferences sharedpreferences = getContext().getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); final String max_id = sharedpreferences.getString(Helper.LAST_HOMETIMELINE_MAX_ID + userId, null); - if( max_id == null){ - SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.LAST_HOMETIMELINE_MAX_ID + userId, apiResponse.getSince_id()); - editor.apply(); - return; - } + //No previous notifications in cache, so no notification will be sent String message; - SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.LAST_HOMETIMELINE_MAX_ID + userId, apiResponse.getSince_id()); - editor.apply(); + for(Status status: statuses){ //The notification associated to max_id is discarded as it is supposed to have already been sent //Also, if the toot comes from the owner, we will avoid to warn him/her... @@ -189,11 +182,17 @@ public class HomeTimelineSyncJob extends Job implements OnRetrieveHomeTimelineSe public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { super.onLoadingComplete(imageUri, view, loadedImage); notify_user(getContext(), intent, notificationId, loadedImage, finalTitle, finalMessage); + SharedPreferences.Editor editor = sharedpreferences.edit(); + editor.putString(Helper.LAST_HOMETIMELINE_MAX_ID + userId, statuses.get(0).getId()); + editor.apply(); } @Override public void onLoadingFailed(java.lang.String imageUri, android.view.View view, FailReason failReason){ notify_user(getContext(), intent, notificationId, BitmapFactory.decodeResource(getContext().getResources(), - R.drawable.mastodonlogo), finalTitle, finalMessage); + R.drawable.mastodonlogo), finalTitle, finalMessage); + SharedPreferences.Editor editor = sharedpreferences.edit(); + editor.putString(Helper.LAST_HOMETIMELINE_MAX_ID + userId, statuses.get(0).getId()); + editor.apply(); }}); } @@ -201,4 +200,4 @@ public class HomeTimelineSyncJob extends Job implements OnRetrieveHomeTimelineSe } -} +} \ No newline at end of file diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/jobs/NotificationsSyncJob.java b/app/src/main/java/fr/gouv/etalab/mastodon/jobs/NotificationsSyncJob.java index 49603c1c1..bca8fe414 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/jobs/NotificationsSyncJob.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/jobs/NotificationsSyncJob.java @@ -133,8 +133,8 @@ public class NotificationsSyncJob extends Job implements OnRetrieveNotifications @Override - public void onRetrieveNotifications(APIResponse apiResponse, String acct, String userId, boolean refreshData) { - List notifications = apiResponse.getNotifications(); + public void onRetrieveNotifications(APIResponse apiResponse, String acct, final String userId, boolean refreshData) { + final List notifications = apiResponse.getNotifications(); if( apiResponse.getError() != null || notifications == null || notifications.size() == 0) return; Bitmap icon_notification = null; @@ -144,12 +144,7 @@ public class NotificationsSyncJob extends Job implements OnRetrieveNotifications boolean notif_mention = sharedpreferences.getBoolean(Helper.SET_NOTIF_MENTION, true); boolean notif_share = sharedpreferences.getBoolean(Helper.SET_NOTIF_SHARE, true); final String max_id = sharedpreferences.getString(Helper.LAST_NOTIFICATION_MAX_ID + userId, null); - if( max_id == null){ - SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + userId, apiResponse.getSince_id()); - editor.apply(); - return; - } + //No previous notifications in cache, so no notification will be sent int newFollows = 0; @@ -176,7 +171,7 @@ public class NotificationsSyncJob extends Job implements OnRetrieveNotifications title = String.format("@%s %s", notification.getAccount().getUsername(),getContext().getString(R.string.notif_mention)); } } - break; + break; case "reblog": if(notif_share){ newShare++; @@ -217,9 +212,7 @@ public class NotificationsSyncJob extends Job implements OnRetrieveNotifications default: } } - SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + userId, apiResponse.getSince_id()); - editor.apply(); + int allNotifCount = newFollows + newAdds + newAsks + newMentions + newShare; if( allNotifCount > 0){ //Some others notification @@ -254,18 +247,26 @@ public class NotificationsSyncJob extends Job implements OnRetrieveNotifications @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { super.onLoadingComplete(imageUri, view, loadedImage); - if( max_id != null) + if( max_id != null) { notify_user(getContext(), intent, notificationId, loadedImage, finalTitle, message); + SharedPreferences.Editor editor = sharedpreferences.edit(); + editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + userId, notifications.get(0).getId()); + editor.apply(); + } } @Override public void onLoadingFailed(java.lang.String imageUri, android.view.View view, FailReason failReason){ - if( max_id != null) + if( max_id != null) { notify_user(getContext(), intent, notificationId, BitmapFactory.decodeResource(getContext().getResources(), R.drawable.mastodonlogo), finalTitle, message); + SharedPreferences.Editor editor = sharedpreferences.edit(); + editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + userId, notifications.get(0).getId()); + editor.apply(); + } }}); } } } -} +} \ No newline at end of file diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/jobs/ScheduledTootsSyncJob.java b/app/src/main/java/fr/gouv/etalab/mastodon/jobs/ScheduledTootsSyncJob.java index 5bc7eb5bf..a995d5b1e 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/jobs/ScheduledTootsSyncJob.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/jobs/ScheduledTootsSyncJob.java @@ -17,7 +17,6 @@ package fr.gouv.etalab.mastodon.jobs; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.support.annotation.NonNull; -import android.util.Log; import com.evernote.android.job.Job; import com.evernote.android.job.JobRequest; diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/services/BootService.java b/app/src/main/java/fr/gouv/etalab/mastodon/services/BootService.java new file mode 100644 index 000000000..147ef37a0 --- /dev/null +++ b/app/src/main/java/fr/gouv/etalab/mastodon/services/BootService.java @@ -0,0 +1,38 @@ +package fr.gouv.etalab.mastodon.services; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.database.sqlite.SQLiteDatabase; + +import java.util.List; + +import fr.gouv.etalab.mastodon.client.Entities.Account; +import fr.gouv.etalab.mastodon.sqlite.AccountDAO; +import fr.gouv.etalab.mastodon.sqlite.Sqlite; + +/** + * Created by Thomas on 29/08/2017. + * BroadcastReceiver to start service when device boot + */ + +public class BootService extends BroadcastReceiver { + + public BootService() { + } + + @Override + public void onReceive(Context context, Intent intent) { + SQLiteDatabase db = Sqlite.getInstance(context, Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + List accounts = new AccountDAO(context, db).getAllAccount(); + if( accounts != null){ + for (Account account: accounts) { + Intent intentService = new Intent(context, StreamingService.class); + intentService.putExtra("acccountId", account.getId()); + intentService.putExtra("accountAcct", account.getAcct()); + context.startService(intentService); + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/services/StreamingService.java b/app/src/main/java/fr/gouv/etalab/mastodon/services/StreamingService.java new file mode 100644 index 000000000..6c26e9683 --- /dev/null +++ b/app/src/main/java/fr/gouv/etalab/mastodon/services/StreamingService.java @@ -0,0 +1,503 @@ +package fr.gouv.etalab.mastodon.services; +/* Copyright 2017 Thomas Schneider + * + * This file is a part of Mastalab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * Mastalab is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even + * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with Mastalab; if not, + * see . */ +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.database.sqlite.SQLiteDatabase; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.os.Build; +import android.os.IBinder; +import android.os.StrictMode; +import android.os.SystemClock; +import android.support.annotation.Nullable; +import android.support.v4.content.LocalBroadcastManager; +import android.text.Html; +import android.util.Log; +import android.view.View; + +import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache; +import com.nostra13.universalimageloader.core.DisplayImageOptions; +import com.nostra13.universalimageloader.core.ImageLoader; +import com.nostra13.universalimageloader.core.ImageLoaderConfiguration; +import com.nostra13.universalimageloader.core.assist.FailReason; +import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer; +import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import javax.net.ssl.HttpsURLConnection; + +import fr.gouv.etalab.mastodon.activities.MainActivity; +import fr.gouv.etalab.mastodon.client.API; +import fr.gouv.etalab.mastodon.client.Entities.Account; +import fr.gouv.etalab.mastodon.client.Entities.Notification; +import fr.gouv.etalab.mastodon.client.Entities.Status; +import fr.gouv.etalab.mastodon.client.PatchBaseImageDownloader; +import fr.gouv.etalab.mastodon.client.TLSSocketFactory; +import fr.gouv.etalab.mastodon.helper.Helper; +import fr.gouv.etalab.mastodon.sqlite.AccountDAO; +import fr.gouv.etalab.mastodon.sqlite.Sqlite; +import mastodon.etalab.gouv.fr.mastodon.R; + +import static fr.gouv.etalab.mastodon.helper.Helper.HOME_TIMELINE_INTENT; +import static fr.gouv.etalab.mastodon.helper.Helper.INTENT_ACTION; +import static fr.gouv.etalab.mastodon.helper.Helper.NOTIFICATION_INTENT; +import static fr.gouv.etalab.mastodon.helper.Helper.PREF_KEY_ID; +import static fr.gouv.etalab.mastodon.helper.Helper.canNotify; +import static fr.gouv.etalab.mastodon.helper.Helper.notify_user; + +/** + * Created by Thomas on 28/08/2017. + * Manage service for streaming api and new notifications + */ + +public class StreamingService extends Service { + + private String message; + private int notificationId; + private Intent intent; + private String lastPreviousContent; + private static HashMap connectionHashMap = new HashMap<>(); + private static HashMap isConnectingHashMap = new HashMap<>(); + private EventStreaming lastEvent; + public enum EventStreaming{ + UPDATE, + NOTIFICATION, + DELETE, + NONE + } + + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + if( intent != null){ + String accountId = intent.getStringExtra("accountId"); + String accountAcct = intent.getStringExtra("accountAcct"); + if( accountId != null && accountAcct != null){ + SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + Account account = new AccountDAO(getApplicationContext(), db).getAccountByIDAcct(accountId, accountAcct); + if( account != null) + callAsynchronousTask(account); + }else { + SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + List accounts = new AccountDAO(getApplicationContext(), db).getAllAccount(); + if( accounts != null){ + for (Account account: accounts) { + intent = new Intent(getApplicationContext(), StreamingService.class); + intent.putExtra("accountId", account.getId()); + intent.putExtra("accountAcct", account.getAcct()); + startService(intent); + } + } + } + } + return START_STICKY; + } + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return null; + } + + + /** + * Task in background starts here. + */ + private void callAsynchronousTask(final Account account) { + //If an Internet connection and user agrees with notification refresh + final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + //Check which notifications the user wants to see + boolean notif_follow = sharedpreferences.getBoolean(Helper.SET_NOTIF_FOLLOW, true); + boolean notif_add = sharedpreferences.getBoolean(Helper.SET_NOTIF_ADD, true); + boolean notif_ask = sharedpreferences.getBoolean(Helper.SET_NOTIF_ASK, true); + boolean notif_mention = sharedpreferences.getBoolean(Helper.SET_NOTIF_MENTION, true); + boolean notif_share = sharedpreferences.getBoolean(Helper.SET_NOTIF_SHARE, true); + //User disagree with all notifications + if( !notif_follow && !notif_add && !notif_ask && !notif_mention && !notif_share) + return; //Nothing is done + //No account connected, the service is stopped + if(!Helper.isLoggedIn(getApplicationContext())) + return; + //If WIFI only and on WIFI OR user defined any connections to use the service. + if( isConnectingHashMap.get(account.getAcct()+account.getId()) != null && isConnectingHashMap.get(account.getAcct()+account.getId())) + return; + if(!sharedpreferences.getBoolean(Helper.SET_WIFI_ONLY, false) || Helper.isOnWIFI(getApplicationContext())) { + Thread readThread = new Thread(new Runnable() { + @Override + public void run() { + try { + boolean connectionAlive = false; + isConnectingHashMap.put(account.getAcct()+account.getId(), true); + if( connectionHashMap.get(account.getAcct()+account.getId()) != null) { + try { + connectionAlive = (connectionHashMap.get(account.getAcct()+account.getId()).getResponseCode() == 200); + } catch (Exception e) { + connectionAlive = false; + } + } + if( connectionAlive) { + HttpsURLConnection httpsURLConnection = connectionHashMap.get(account.getAcct() + account.getId()); + if( httpsURLConnection != null) + httpsURLConnection.disconnect(); + } + try { + URL url = new URL("https://" + account.getInstance() + "/api/v1/streaming/user"); + HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection(); + urlConnection.setRequestProperty("Content-Type", "application/json"); + urlConnection.setRequestProperty("Authorization", "Bearer " + account.getToken()); + urlConnection.setRequestProperty("Connection", "Keep-Alive"); + urlConnection.setRequestProperty("Keep-Alive", "header"); + urlConnection.setRequestProperty("Connection", "close"); + urlConnection.setSSLSocketFactory(new TLSSocketFactory()); + connectionHashMap.put(account.getAcct()+account.getId(), urlConnection); + InputStream inputStream = new BufferedInputStream(urlConnection.getInputStream()); + readStream(inputStream, account); + } catch (IOException | NoSuchAlgorithmException | KeyManagementException e) { + e.printStackTrace(); + forceRestart(account); + } + } catch (Exception ignored) { + } + } + }); + readThread.start(); + } + } + + + + + + + + @SuppressWarnings("ConstantConditions") + private String readStream(InputStream inputStream, final Account account) { + BufferedReader reader = null; + try{ + reader = new BufferedReader(new InputStreamReader(inputStream)); + String event; + EventStreaming eventStreaming = null; + //noinspection InfiniteLoopStatement + while(true){ + try { + event = reader.readLine(); + }catch (Exception e){ + e.printStackTrace(); + forceRestart(account); + break; + } + if (event !=null){ + if( (lastEvent == EventStreaming.NONE || lastEvent == null) && !event.startsWith("data: ")) { + switch (event.trim()) { + case "event: update": + lastEvent = EventStreaming.UPDATE; + break; + case "event: notification": + lastEvent = EventStreaming.NOTIFICATION; + break; + case "event: delete": + lastEvent = EventStreaming.DELETE; + break; + default: + lastEvent = EventStreaming.NONE; + } + }else{ + if( !event.startsWith("data: ")){ + lastEvent = EventStreaming.NONE; + continue; + } + event = event.substring(6); + if(lastEvent == EventStreaming.UPDATE) { + eventStreaming = EventStreaming.UPDATE; + }else if(lastEvent == EventStreaming.NOTIFICATION) { + eventStreaming = EventStreaming.NOTIFICATION; + }else if( lastEvent == EventStreaming.DELETE) { + eventStreaming = EventStreaming.DELETE; + event = "{id:" + event + "}"; + }else { + eventStreaming = EventStreaming.UPDATE; + } + lastEvent = EventStreaming.NONE; + try { + JSONObject eventJson = new JSONObject(event); + onRetrieveStreaming(eventStreaming, eventJson, account.getAcct(), account.getId()); + } catch (JSONException e) { + e.printStackTrace(); + } + } + } + } + }catch (Exception e){ + e.printStackTrace(); + }finally { + if(reader != null){ + try{ + reader.close(); + }catch (IOException e){ + e.printStackTrace(); + } + } + forceRestart(account); + } + return null; + } + + private void forceRestart(Account account){ + isConnectingHashMap.put(account.getAcct()+account.getId(), false); + SystemClock.sleep(1000); + Intent intent = new Intent(getApplicationContext(), StreamingService.class); + intent.putExtra("accountId", account.getId()); + intent.putExtra("accountAcct", account.getAcct()); + startService(intent); + } + + @Override + public void onTaskRemoved(Intent rootIntent) { + Intent intent = new Intent(getApplicationContext(), StreamingService.class); + PendingIntent pendingIntent = PendingIntent.getService(this, 1, intent, PendingIntent.FLAG_ONE_SHOT); + AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); + alarmManager.set(AlarmManager.RTC_WAKEUP, SystemClock.elapsedRealtime() + 5000, pendingIntent); + super.onTaskRemoved(rootIntent); + } + + + public void onRetrieveStreaming(EventStreaming event, JSONObject response, String acct, String userId) { + if( response == null ) + return; + final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + boolean notif_follow = sharedpreferences.getBoolean(Helper.SET_NOTIF_FOLLOW, true); + boolean notif_add = sharedpreferences.getBoolean(Helper.SET_NOTIF_ADD, true); + boolean notif_mention = sharedpreferences.getBoolean(Helper.SET_NOTIF_MENTION, true); + boolean notif_share = sharedpreferences.getBoolean(Helper.SET_NOTIF_SHARE, true); + + //No previous notifications in cache, so no notification will be sent + boolean notify = false; + String notificationUrl = null; + String title = null; + Status status = null; + Notification notification = null; + String dataId = null; + if( event == EventStreaming.NOTIFICATION){ + notification = API.parseNotificationResponse(getApplicationContext(), response); + switch (notification.getType()){ + case "mention": + if(notif_mention){ + lastPreviousContent = notification.getStatus().getContent(); + notify = true; + notificationUrl = notification.getAccount().getAvatar(); + if( notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0 ) + title = String.format("%s %s", Helper.shortnameToUnicode(notification.getAccount().getDisplay_name(), true),getString(R.string.notif_mention)); + else + title = String.format("%s %s", notification.getAccount().getUsername(),getString(R.string.notif_mention)); + } + break; + case "reblog": + if(notif_share){ + notify = true; + notificationUrl = notification.getAccount().getAvatar(); + if( notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0 ) + title = String.format("%s %s", Helper.shortnameToUnicode(notification.getAccount().getDisplay_name(), true),getString(R.string.notif_reblog)); + else + title = String.format("%s %s", notification.getAccount().getUsername(),getString(R.string.notif_reblog)); + } + break; + case "favourite": + if(notif_add){ + notify = true; + notificationUrl = notification.getAccount().getAvatar(); + if( notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0 ) + title = String.format("%s %s", Helper.shortnameToUnicode(notification.getAccount().getDisplay_name(), true),getString(R.string.notif_favourite)); + else + title = String.format("%s %s", notification.getAccount().getUsername(),getString(R.string.notif_favourite)); + } + break; + case "follow": + if(notif_follow){ + notify = true; + notificationUrl = notification.getAccount().getAvatar(); + if( notification.getAccount().getDisplay_name() != null && notification.getAccount().getDisplay_name().length() > 0 ) + title = String.format("%s %s", Helper.shortnameToUnicode(notification.getAccount().getDisplay_name(), true),getString(R.string.notif_follow)); + else + title = String.format("%s %s", notification.getAccount().getUsername(),getString(R.string.notif_follow)); + } + break; + default: + break; + } + Helper.cacheNotifications(getApplicationContext(), notification, userId); + if( notification.getStatus().getContent()!= null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) + message = Html.fromHtml(notification.getStatus().getContent(), Html.FROM_HTML_MODE_LEGACY).toString(); + else + //noinspection deprecation + message = Html.fromHtml(notification.getStatus().getContent()).toString(); + message = message.substring(0, message.length()>49?49:message.length()); + message = message + "…"; + }else{ + message = ""; + } + + }else if ( event == EventStreaming.UPDATE){ + status = API.parseStatuses(getApplicationContext(), response); + status.setReplies(new ArrayList()); //Force to don't display replies + status.setNew(true); + Helper.cacheStatus(getApplicationContext(), status, userId); + if( status.getContent() != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) + message = Html.fromHtml(status.getContent(), Html.FROM_HTML_MODE_LEGACY).toString(); + else + //noinspection deprecation + message = Html.fromHtml(status.getContent()).toString(); + message = message.substring(0, message.length()>49?49:message.length()); + message = message + "…"; + }else{ + message = ""; + } + title = getString(R.string.notif_pouet, status.getAccount().getUsername()); + notificationUrl = status.getAccount().getAvatar(); + }else if( event == EventStreaming.DELETE){ + try { + dataId = response.getString("id"); + } catch (JSONException e) { + e.printStackTrace(); + } + } + + //Check which user is connected and if activity is to front + boolean activityVisible = false; + try{ + activityVisible = MainActivity.isActivityVisible(); + }catch (Exception ignored){} + String connectedUser = sharedpreferences.getString(Helper.PREF_KEY_ID, null); + SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + Account account = new AccountDAO(getApplicationContext(), db).getAccountByID(connectedUser); + //User receiving the notification is connected + if( isCurrentAccountLoggedIn(acct, userId)){ + notify = false; + Intent intentBC = new Intent(Helper.RECEIVE_DATA); + intentBC.putExtra("eventStreaming", event); + LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intentBC); + } + //User receiving the notification is connected and application is to front, notification won't be pushed + //Instead, the interaction is done in the activity + if( activityVisible && isCurrentAccountLoggedIn(acct, userId)){ + notify = false; + }else if(event == EventStreaming.NOTIFICATION ){ + notify = true; + }else if(event == EventStreaming.UPDATE ){ + //lastPreviousContent contains the content of the last notification, if it was a mention it will avoid to push two notifications + if( account == null || (lastPreviousContent != null && lastPreviousContent.equals(status.getContent()))) { + notify = false; + }else { + notify = true; + //Retrieve users in db that owner has, and if the toot matches one of them we don't notify + List accounts = new AccountDAO(getApplicationContext(),db).getAllAccount(); + for(Account act_tmp: accounts) { + if(notify && act_tmp.getAcct().trim().equals(status.getAccount().getAcct()) && act_tmp.getId().trim().equals(status.getAccount().getId().trim())){ + notify = false; + } + } + //Here we check if the user wants home timeline notifications + if( notify ) + notify = sharedpreferences.getBoolean(Helper.SET_NOTIF_HOMETIMELINE, true); + } + lastPreviousContent = status.getContent(); + } + //All is good here for a notification, we will know check if it can be done depending of the hour + if( notify) + notify = canNotify(getApplicationContext()); + if( notify && event == EventStreaming.NOTIFICATION){ + intent = new Intent(getApplicationContext(), MainActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK ); + intent.putExtra(INTENT_ACTION, NOTIFICATION_INTENT); + intent.putExtra(PREF_KEY_ID, userId); + long notif_id = Long.parseLong(userId); + notificationId = ((notif_id + 1) > 2147483647) ? (int) (2147483647 - notif_id - 1) : (int) (notif_id + 1); + SharedPreferences.Editor editor = sharedpreferences.edit(); + editor.putString(Helper.LAST_NOTIFICATION_MAX_ID + userId, notification.getId()); + editor.apply(); + } + if( notify && event == EventStreaming.UPDATE) { + intent = new Intent(getApplicationContext(), MainActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(INTENT_ACTION, HOME_TIMELINE_INTENT); + intent.putExtra(PREF_KEY_ID, userId); + long notif_id = Long.parseLong(userId); + notificationId = ((notif_id + 2) > 2147483647) ? (int) (2147483647 - notif_id - 2) : (int) (notif_id + 2); + SharedPreferences.Editor editor = sharedpreferences.edit(); + editor.putString(Helper.LAST_HOMETIMELINE_MAX_ID + userId, status.getId()); + editor.apply(); + } + + if( notify){ + if( notificationUrl != null){ + ImageLoader imageLoaderNoty = ImageLoader.getInstance(); + File cacheDir = new File(getApplicationContext().getCacheDir(), getString(R.string.app_name)); + ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext()) + .imageDownloader(new PatchBaseImageDownloader(getApplicationContext())) + .threadPoolSize(5) + .threadPriority(Thread.MIN_PRIORITY + 3) + .denyCacheImageMultipleSizesInMemory() + .diskCache(new UnlimitedDiskCache(cacheDir)) + .build(); + imageLoaderNoty.init(config); + DisplayImageOptions options = new DisplayImageOptions.Builder().displayer(new SimpleBitmapDisplayer()).cacheInMemory(false) + .cacheOnDisk(true).resetViewBeforeLoading(true).build(); + + final String finalTitle = title; + imageLoaderNoty.loadImage(notificationUrl, options, new SimpleImageLoadingListener(){ + @Override + public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { + super.onLoadingComplete(imageUri, view, loadedImage); + notify_user(getApplicationContext(), intent, notificationId, loadedImage, finalTitle, message); + + } + @Override + public void onLoadingFailed(String imageUri, View view, FailReason failReason){ + notify_user(getApplicationContext(), intent, notificationId, BitmapFactory.decodeResource(getApplicationContext().getResources(), + R.drawable.mastodonlogo), finalTitle, message); + }}); + } + } + } + + private boolean isCurrentAccountLoggedIn(String acct, String userId){ + final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + String userconnected = sharedpreferences.getString(Helper.PREF_KEY_ID, null); + SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + Account account = new AccountDAO(getApplicationContext(), db).getAccountByID(userconnected); + return acct.trim().equals(account.getAcct().trim()) && userId.trim().equals(account.getId().trim()); + } +} diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/sqlite/AccountDAO.java b/app/src/main/java/fr/gouv/etalab/mastodon/sqlite/AccountDAO.java index e0d46ebc8..4477d2f0e 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/sqlite/AccountDAO.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/sqlite/AccountDAO.java @@ -133,6 +133,21 @@ public class AccountDAO { } } + /** + * Returns an Account by its id and acct + * @param accountId String + * @param accountAcct String + * @return Account + */ + public Account getAccountByIDAcct(String accountId, String accountAcct){ + try { + Cursor c = db.query(Sqlite.TABLE_USER_ACCOUNT, null, Sqlite.COL_USER_ID + " = '" + accountId + "' AND " + Sqlite.COL_ACCT + " = '" + accountAcct + "'", null, null, null, null, "1"); + return cursorToUser(c); + } catch (Exception e) { + return null; + } + } + /** * Returns an Account by id and instance * @param accountId String diff --git a/app/src/main/res/drawable-hdpi/ic_edit.png b/app/src/main/res/drawable-hdpi/ic_edit.png new file mode 100644 index 000000000..e19f4dd91 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_edit.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_fav_header.png b/app/src/main/res/drawable-hdpi/ic_fav_header.png new file mode 100644 index 000000000..0e8c7e9a1 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_fav_header.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_fav_notif_header.png b/app/src/main/res/drawable-hdpi/ic_fav_notif_header.png new file mode 100644 index 000000000..0e8c7e9a1 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_fav_notif_header.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_fiber_new.png b/app/src/main/res/drawable-hdpi/ic_fiber_new.png new file mode 100644 index 000000000..ec7fdc454 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_fiber_new.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_follow_header.png b/app/src/main/res/drawable-hdpi/ic_follow_header.png new file mode 100644 index 000000000..46cfb9fe8 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_follow_header.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_follow_notif_header.png b/app/src/main/res/drawable-hdpi/ic_follow_notif_header.png new file mode 100644 index 000000000..46cfb9fe8 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_follow_notif_header.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_retweet_header.png b/app/src/main/res/drawable-hdpi/ic_retweet_header.png new file mode 100644 index 000000000..8aa0ff8dd Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_retweet_header.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_retweet_notif_header.png b/app/src/main/res/drawable-hdpi/ic_retweet_notif_header.png new file mode 100644 index 000000000..8aa0ff8dd Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_retweet_notif_header.png differ diff --git a/app/src/main/res/drawable-ldpi/ic_edit.png b/app/src/main/res/drawable-ldpi/ic_edit.png new file mode 100644 index 000000000..1d75bd154 Binary files /dev/null and b/app/src/main/res/drawable-ldpi/ic_edit.png differ diff --git a/app/src/main/res/drawable-ldpi/ic_fav_header.png b/app/src/main/res/drawable-ldpi/ic_fav_header.png new file mode 100644 index 000000000..b69764624 Binary files /dev/null and b/app/src/main/res/drawable-ldpi/ic_fav_header.png differ diff --git a/app/src/main/res/drawable-ldpi/ic_fav_notif_header.png b/app/src/main/res/drawable-ldpi/ic_fav_notif_header.png new file mode 100644 index 000000000..b69764624 Binary files /dev/null and b/app/src/main/res/drawable-ldpi/ic_fav_notif_header.png differ diff --git a/app/src/main/res/drawable-ldpi/ic_fiber_new.png b/app/src/main/res/drawable-ldpi/ic_fiber_new.png new file mode 100644 index 000000000..45be937c6 Binary files /dev/null and b/app/src/main/res/drawable-ldpi/ic_fiber_new.png differ diff --git a/app/src/main/res/drawable-ldpi/ic_follow_header.png b/app/src/main/res/drawable-ldpi/ic_follow_header.png new file mode 100644 index 000000000..805a8d518 Binary files /dev/null and b/app/src/main/res/drawable-ldpi/ic_follow_header.png differ diff --git a/app/src/main/res/drawable-ldpi/ic_follow_notif_header.png b/app/src/main/res/drawable-ldpi/ic_follow_notif_header.png new file mode 100644 index 000000000..805a8d518 Binary files /dev/null and b/app/src/main/res/drawable-ldpi/ic_follow_notif_header.png differ diff --git a/app/src/main/res/drawable-ldpi/ic_retweet_notif_header.png b/app/src/main/res/drawable-ldpi/ic_retweet_notif_header.png new file mode 100644 index 000000000..5ec2cfb26 Binary files /dev/null and b/app/src/main/res/drawable-ldpi/ic_retweet_notif_header.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_edit.png b/app/src/main/res/drawable-mdpi/ic_edit.png new file mode 100644 index 000000000..5f68300d4 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_edit.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_fav_header.png b/app/src/main/res/drawable-mdpi/ic_fav_header.png new file mode 100644 index 000000000..8b4b7ec79 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_fav_header.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_fav_notif_header.png b/app/src/main/res/drawable-mdpi/ic_fav_notif_header.png new file mode 100644 index 000000000..8b4b7ec79 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_fav_notif_header.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_fiber_new.png b/app/src/main/res/drawable-mdpi/ic_fiber_new.png new file mode 100644 index 000000000..589f71501 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_fiber_new.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_follow_header.png b/app/src/main/res/drawable-mdpi/ic_follow_header.png new file mode 100644 index 000000000..3f6c75c56 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_follow_header.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_follow_notif_header.png b/app/src/main/res/drawable-mdpi/ic_follow_notif_header.png new file mode 100644 index 000000000..3f6c75c56 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_follow_notif_header.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_retweet_notif_header.png b/app/src/main/res/drawable-mdpi/ic_retweet_notif_header.png new file mode 100644 index 000000000..d12a55f15 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_retweet_notif_header.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_edit.png b/app/src/main/res/drawable-xhdpi/ic_edit.png new file mode 100644 index 000000000..3ecfd46f1 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_edit.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_fav_header.png b/app/src/main/res/drawable-xhdpi/ic_fav_header.png new file mode 100644 index 000000000..71d0bfab0 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_fav_header.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_fav_notif_header.png b/app/src/main/res/drawable-xhdpi/ic_fav_notif_header.png new file mode 100644 index 000000000..71d0bfab0 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_fav_notif_header.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_fiber_new.png b/app/src/main/res/drawable-xhdpi/ic_fiber_new.png new file mode 100644 index 000000000..08208c303 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_fiber_new.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_follow_header.png b/app/src/main/res/drawable-xhdpi/ic_follow_header.png new file mode 100644 index 000000000..77c482e5b Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_follow_header.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_follow_notif_header.png b/app/src/main/res/drawable-xhdpi/ic_follow_notif_header.png new file mode 100644 index 000000000..77c482e5b Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_follow_notif_header.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_retweet_notif_header.png b/app/src/main/res/drawable-xhdpi/ic_retweet_notif_header.png new file mode 100644 index 000000000..79c8052c9 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_retweet_notif_header.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_edit.png b/app/src/main/res/drawable-xxhdpi/ic_edit.png new file mode 100644 index 000000000..bfd9e3b60 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_edit.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_fav_header.png b/app/src/main/res/drawable-xxhdpi/ic_fav_header.png new file mode 100644 index 000000000..70f761c8d Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_fav_header.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_fav_notif_header.png b/app/src/main/res/drawable-xxhdpi/ic_fav_notif_header.png new file mode 100644 index 000000000..70f761c8d Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_fav_notif_header.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_fiber_new.png b/app/src/main/res/drawable-xxhdpi/ic_fiber_new.png new file mode 100644 index 000000000..160387cdd Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_fiber_new.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_follow_header.png b/app/src/main/res/drawable-xxhdpi/ic_follow_header.png new file mode 100644 index 000000000..e1f60cc22 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_follow_header.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_follow_notif_header.png b/app/src/main/res/drawable-xxhdpi/ic_follow_notif_header.png new file mode 100644 index 000000000..e1f60cc22 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_follow_notif_header.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_retweet_notif_header.png b/app/src/main/res/drawable-xxhdpi/ic_retweet_notif_header.png new file mode 100644 index 000000000..0fc18c78a Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_retweet_notif_header.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_edit.png b/app/src/main/res/drawable-xxxhdpi/ic_edit.png new file mode 100644 index 000000000..29046d95c Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_edit.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_fav_header.png b/app/src/main/res/drawable-xxxhdpi/ic_fav_header.png new file mode 100644 index 000000000..154f6c446 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_fav_header.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_fav_notif_header.png b/app/src/main/res/drawable-xxxhdpi/ic_fav_notif_header.png new file mode 100644 index 000000000..154f6c446 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_fav_notif_header.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_fiber_new.png b/app/src/main/res/drawable-xxxhdpi/ic_fiber_new.png new file mode 100644 index 000000000..30dacde3c Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_fiber_new.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_follow_header.png b/app/src/main/res/drawable-xxxhdpi/ic_follow_header.png new file mode 100644 index 000000000..ffa613901 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_follow_header.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_follow_notif_header.png b/app/src/main/res/drawable-xxxhdpi/ic_follow_notif_header.png new file mode 100644 index 000000000..ffa613901 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_follow_notif_header.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_retweet.png b/app/src/main/res/drawable-xxxhdpi/ic_retweet_notif_header.png similarity index 100% rename from app/src/main/res/drawable-xxxhdpi/ic_retweet.png rename to app/src/main/res/drawable-xxxhdpi/ic_retweet_notif_header.png diff --git a/app/src/main/res/layout-sw600dp/activity_about.xml b/app/src/main/res/layout-sw600dp/activity_about.xml index 1ebba5a63..f6cde8384 100644 --- a/app/src/main/res/layout-sw600dp/activity_about.xml +++ b/app/src/main/res/layout-sw600dp/activity_about.xml @@ -16,9 +16,9 @@ see . --> - + + - -