Merged tom79/mastodon_etalab into master

This commit is contained in:
PhotonQyv 2017-09-10 17:46:11 +01:00
commit 00ccdc42c9
106 changed files with 3650 additions and 1185 deletions

BIN
app/app-fdroid-release.apk Normal file

Binary file not shown.

View File

@ -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 {

View File

@ -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<String, String> 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<Account> 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;
}

View File

@ -24,6 +24,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:name="fr.gouv.etalab.mastodon.activities.MainApplication"
@ -34,10 +35,18 @@
android:roundIcon="@mipmap/ic_launcher"
android:supportsRtl="true"
android:theme="@style/AppThemeDark">
<service
android:name="fr.gouv.etalab.mastodon.services.StreamingService"
android:exported="false"/>
<receiver android:name="fr.gouv.etalab.mastodon.services.BootService" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<activity
android:name="fr.gouv.etalab.mastodon.activities.MainActivity"
android:label="@string/app_name"
android:launchMode="singleTop"
android:launchMode="singleTask"
android:windowSoftInputMode = "adjustResize"
android:configChanges="keyboardHidden|orientation|screenSize"
android:theme="@style/AppThemeDark_NoActionBar">
@ -70,7 +79,6 @@
<activity android:name="fr.gouv.etalab.mastodon.activities.HashTagActivity"
android:windowSoftInputMode="stateAlwaysHidden"
android:configChanges="orientation|screenSize"
android:noHistory="true"
android:label="@string/app_name"
/>
<activity android:name="fr.gouv.etalab.mastodon.activities.WebviewConnectActivity"
@ -82,7 +90,6 @@
android:label="@string/app_name"
android:configChanges="keyboardHidden|orientation|screenSize"
android:launchMode="singleTask"
android:noHistory="true"
/>
<activity android:name="fr.gouv.etalab.mastodon.activities.MediaActivity"
android:label="@string/app_name"
@ -127,6 +134,11 @@
android:label="@string/app_name"
android:theme="@style/AppTheme_NoActionBar"
/>
<activity android:name="fr.gouv.etalab.mastodon.activities.EditProfileActivity"
android:windowSoftInputMode="stateAlwaysHidden"
android:configChanges="orientation|screenSize"
android:label="@string/app_name"
/>
<activity
android:name="fr.gouv.etalab.mastodon.activities.SplashActivity"
android:windowSoftInputMode="stateAlwaysHidden"

View File

@ -2424,4 +2424,261 @@ digit_one, 0x0031
digit_zero, 0x0030
family_man_woman_boy, 0x1f468,0x1f469,0x1f466
couple_with_heart_woman_man, 0x1f469,0x2764,0x1f468
kiss_woman_man, 0x1f469,0x2764,0x1f48b,0x1f468
kiss_woman_man, 0x1f469,0x2764,0x1f48b,0x1f468
ad, 0x1f1e6,0x1f1e9
ao, 0x1f1e6,0x1f1f4
af, 0x1f1e6,0x1f1eb
ai, 0x1f1e6,0x1f1ee
aq, 0x1f1e6,0x1f1f6
ag, 0x1f1e6,0x1f1ec
al, 0x1f1e6,0x1f1f1
ar, 0x1f1e6,0x1f1f7
as, 0x1f1e6,0x1f1f8
am, 0x1f1e6,0x1f1f2
aw, 0x1f1e6,0x1f1fc
au, 0x1f1e6,0x1f1fa
at, 0x1f1e6,0x1f1f9
ax, 0x1f1e6,0x1f1fd
az, 0x1f1e6,0x1f1ff
bs, 0x1f1e7,0x1f1f8
bh, 0x1f1e7,0x1f1ed
bd, 0x1f1e7,0x1f1e9
bb, 0x1f1e7,0x1f1e7
by, 0x1f1e7,0x1f1fe
be, 0x1f1e7,0x1f1ea
bz, 0x1f1e7,0x1f1ff
bj, 0x1f1e7,0x1f1ef
bm, 0x1f1e7,0x1f1f2
bt, 0x1f1e7,0x1f1f9
bo, 0x1f1e7,0x1f1f4
ba, 0x1f1e7,0x1f1e6
bw, 0x1f1e7,0x1f1fc
br, 0x1f1e7,0x1f1f7
io, 0x1f1ee,0x1f1f4
vg, 0x1f1fb,0x1f1ec
bn, 0x1f1e7,0x1f1f3
bg, 0x1f1e7,0x1f1ec
bf, 0x1f1e7,0x1f1eb
bi, 0x1f1e7,0x1f1ee
kh, 0x1f1f0,0x1f1ed
cm, 0x1f1e8,0x1f1f2
ca, 0x1f1e8,0x1f1e6
ic, 0x1f1ee,0x1f1e8
cv, 0x1f1e8,0x1f1fb
bq, 0x1f1e7,0x1f1f6
ky, 0x1f1f0,0x1f1fe
cf, 0x1f1e8,0x1f1eb
td, 0x1f1f9,0x1f1e9
cl, 0x1f1e8,0x1f1f1
cn, 0x1f1e8,0x1f1f3
cx, 0x1f1e8,0x1f1fd
cc, 0x1f1e8,0x1f1e8
co, 0x1f1e8,0x1f1f4
km, 0x1f1f0,0x1f1f2
cg, 0x1f1e8,0x1f1ec
cd, 0x1f1e8,0x1f1e9
ck, 0x1f1e8,0x1f1f0
cr, 0x1f1e8,0x1f1f7
ci, 0x1f1e8,0x1f1ee
hr, 0x1f1ed,0x1f1f7
cu, 0x1f1e8,0x1f1fa
cw, 0x1f1e8,0x1f1fc
cy, 0x1f1e8,0x1f1fe
cz, 0x1f1e8,0x1f1ff
dk, 0x1f1e9,0x1f1f0
dj, 0x1f1e9,0x1f1ef
dm, 0x1f1e9,0x1f1f2
do, 0x1f1e9,0x1f1f4
ec, 0x1f1ea,0x1f1e8
eg, 0x1f1ea,0x1f1ec
sv, 0x1f1f8,0x1f1fb
gq, 0x1f1ec,0x1f1f6
er, 0x1f1ea,0x1f1f7
ee, 0x1f1ea,0x1f1ea
et, 0x1f1ea,0x1f1f9
eu, 0x1f1ea,0x1f1fa
fk, 0x1f1eb,0x1f1f0
fo, 0x1f1eb,0x1f1f4
fj, 0x1f1eb,0x1f1ef
fi, 0x1f1eb,0x1f1ee
fr, 0x1f1eb,0x1f1f7
gf, 0x1f1ec,0x1f1eb
pf, 0x1f1f5,0x1f1eb
tf, 0x1f1f9,0x1f1eb
ga, 0x1f1ec,0x1f1e6
gb, 0x1f1ec,0x1f1e7
gm, 0x1f1ec,0x1f1f2
ge, 0x1f1ec,0x1f1ea
de, 0x1f1e9,0x1f1ea
gh, 0x1f1ec,0x1f1ed
gi, 0x1f1ec,0x1f1ee
gr, 0x1f1ec,0x1f1f7
gl, 0x1f1ec,0x1f1f1
gd, 0x1f1ec,0x1f1e9
gp, 0x1f1ec,0x1f1f5
gu, 0x1f1ec,0x1f1fa
gt, 0x1f1ec,0x1f1f9
gg, 0x1f1ec,0x1f1ec
gn, 0x1f1ec,0x1f1f3
gw, 0x1f1ec,0x1f1fc
gy, 0x1f1ec,0x1f1fe
ht, 0x1f1ed,0x1f1f9
hn, 0x1f1ed,0x1f1f3
hk, 0x1f1ed,0x1f1f0
hu, 0x1f1ed,0x1f1fa
is, 0x1f1ee,0x1f1f8
in, 0x1f1ee,0x1f1f3
id, 0x1f1ee,0x1f1e9
ir, 0x1f1ee,0x1f1f7
iq, 0x1f1ee,0x1f1f6
ie, 0x1f1ee,0x1f1ea
im, 0x1f1ee,0x1f1f2
il, 0x1f1ee,0x1f1f1
it, 0x1f1ee,0x1f1f9
jm, 0x1f1ef,0x1f1f2
jp, 0x1f1ef,0x1f1f5
je, 0x1f1ef,0x1f1ea
jo, 0x1f1ef,0x1f1f4
kz, 0x1f1f0,0x1f1ff
ke, 0x1f1f0,0x1f1ea
ki, 0x1f1f0,0x1f1ee
xk, 0x1f1fd,0x1f1f0
kw, 0x1f1f0,0x1f1fc
kg, 0x1f1f0,0x1f1ec
la, 0x1f1f1,0x1f1e6
lv, 0x1f1f1,0x1f1fb
lb, 0x1f1f1,0x1f1e7
ls, 0x1f1f1,0x1f1f8
lr, 0x1f1f1,0x1f1f7
ly, 0x1f1f1,0x1f1fe
li, 0x1f1f1,0x1f1ee
lt, 0x1f1f1,0x1f1f9
lu, 0x1f1f1,0x1f1fa
mo, 0x1f1f2,0x1f1f4
mk, 0x1f1f2,0x1f1f0
mg, 0x1f1f2,0x1f1ec
mw, 0x1f1f2,0x1f1fc
my, 0x1f1f2,0x1f1fe
mv, 0x1f1f2,0x1f1fb
ml, 0x1f1f2,0x1f1f1
mt, 0x1f1f2,0x1f1f9
mh, 0x1f1f2,0x1f1ed
mq, 0x1f1f2,0x1f1f6
mr, 0x1f1f2,0x1f1f7
mu, 0x1f1f2,0x1f1fa
yt, 0x1f1fe,0x1f1f9
mx, 0x1f1f2,0x1f1fd
fm, 0x1f1eb,0x1f1f2
md, 0x1f1f2,0x1f1e9
mc, 0x1f1f2,0x1f1e8
mn, 0x1f1f2,0x1f1f3
me, 0x1f1f2,0x1f1ea
ms, 0x1f1f2,0x1f1f8
ma, 0x1f1f2,0x1f1e6
mz, 0x1f1f2,0x1f1ff
mm, 0x1f1f2,0x1f1f2
na, 0x1f1f3,0x1f1e6
nr, 0x1f1f3,0x1f1f7
np, 0x1f1f3,0x1f1f5
nl, 0x1f1f3,0x1f1f1
nc, 0x1f1f3,0x1f1e8
nz, 0x1f1f3,0x1f1ff
ni, 0x1f1f3,0x1f1ee
ne, 0x1f1f3,0x1f1ea
ng, 0x1f1f3,0x1f1ec
nu, 0x1f1f3,0x1f1fa
nf, 0x1f1f3,0x1f1eb
kp, 0x1f1f0,0x1f1f5
mp, 0x1f1f2,0x1f1f5
no, 0x1f1f3,0x1f1f4
om, 0x1f1f4,0x1f1f2
pk, 0x1f1f5,0x1f1f0
pw, 0x1f1f5,0x1f1fc
ps, 0x1f1f5,0x1f1f8
pa, 0x1f1f5,0x1f1e6
pg, 0x1f1f5,0x1f1ec
py, 0x1f1f5,0x1f1fe
pe, 0x1f1f5,0x1f1ea
ph, 0x1f1f5,0x1f1ed
pn, 0x1f1f5,0x1f1f3
pl, 0x1f1f5,0x1f1f1
pt, 0x1f1f5,0x1f1f9
pr, 0x1f1f5,0x1f1f7
qa, 0x1f1f6,0x1f1e6
re, 0x1f1f7,0x1f1ea
ro, 0x1f1f7,0x1f1f4
ru, 0x1f1f7,0x1f1fa
rw, 0x1f1f7,0x1f1fc
ws, 0x1f1fc,0x1f1f8
sm, 0x1f1f8,0x1f1f2
st, 0x1f1f8,0x1f1f9
sa, 0x1f1f8,0x1f1e6
sn, 0x1f1f8,0x1f1f3
rs, 0x1f1f7,0x1f1f8
sc, 0x1f1f8,0x1f1e8
sl, 0x1f1f8,0x1f1f1
sg, 0x1f1f8,0x1f1ec
sx, 0x1f1f8,0x1f1fd
sk, 0x1f1f8,0x1f1f0
si, 0x1f1f8,0x1f1ee
gs, 0x1f1ec,0x1f1f8
sb, 0x1f1f8,0x1f1e7
so, 0x1f1f8,0x1f1f4
za, 0x1f1ff,0x1f1e6
kr, 0x1f1f0,0x1f1f7
ss, 0x1f1f8,0x1f1f8
es, 0x1f1ea,0x1f1f8
lk, 0x1f1f1,0x1f1f0
bl, 0x1f1e7,0x1f1f1
sh, 0x1f1f8,0x1f1ed
kn, 0x1f1f0,0x1f1f3
lc, 0x1f1f1,0x1f1e8
pm, 0x1f1f5,0x1f1f2
vc, 0x1f1fb,0x1f1e8
sd, 0x1f1f8,0x1f1e9
sr, 0x1f1f8,0x1f1f7
sz, 0x1f1f8,0x1f1ff
se, 0x1f1f8,0x1f1ea
ch, 0x1f1e8,0x1f1ed
sy, 0x1f1f8,0x1f1fe
tw, 0x1f1f9,0x1f1fc
tj, 0x1f1f9,0x1f1ef
tz, 0x1f1f9,0x1f1ff
th, 0x1f1f9,0x1f1ed
tl, 0x1f1f9,0x1f1f1
tg, 0x1f1f9,0x1f1ec
tk, 0x1f1f9,0x1f1f0
to, 0x1f1f9,0x1f1f4
tt, 0x1f1f9,0x1f1f9
tn, 0x1f1f9,0x1f1f3
tr, 0x1f1f9,0x1f1f7
tm, 0x1f1f9,0x1f1f2
tc, 0x1f1f9,0x1f1e8
tv, 0x1f1f9,0x1f1fb
vi, 0x1f1fb,0x1f1ee
ug, 0x1f1fa,0x1f1ec
ua, 0x1f1fa,0x1f1e6
ae, 0x1f1e6,0x1f1ea
us, 0x1f1fa,0x1f1f8
uy, 0x1f1fa,0x1f1fe
uz, 0x1f1fa,0x1f1ff
vu, 0x1f1fb,0x1f1fa
va, 0x1f1fb,0x1f1e6
ve, 0x1f1fb,0x1f1ea
vn, 0x1f1fb,0x1f1f3
wf, 0x1f1fc,0x1f1eb
eh, 0x1f1ea,0x1f1ed
ye, 0x1f1fe,0x1f1ea
zm, 0x1f1ff,0x1f1f2
zw, 0x1f1ff,0x1f1fc
ac, 0x1f1e6,0x1f1e8
ta, 0x1f1f9,0x1f1e6
bv, 0x1f1e7,0x1f1fb
hm, 0x1f1ed,0x1f1f2
sj, 0x1f1f8,0x1f1ef
um, 0x1f1fa,0x1f1f2
ea, 0x1f1ea,0x1f1e6
cp, 0x1f1e8,0x1f1f5
dg, 0x1f1e9,0x1f1ec
dz, 0x1f1e9,0x1f1ff
mf, 0x1f1f2,0x1f1eb
Can't render this file because it has a wrong number of fields in line 168.

View File

@ -23,23 +23,27 @@ import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.method.LinkMovementMethod;
import android.text.style.ClickableSpan;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import fr.gouv.etalab.mastodon.asynctasks.RetrieveDeveloperAccountsAsyncTask;
import fr.gouv.etalab.mastodon.client.APIResponse;
import fr.gouv.etalab.mastodon.asynctasks.RetrieveRelationshipAsyncTask;
import fr.gouv.etalab.mastodon.asynctasks.RetrieveRemoteAccountsAsyncTask;
import fr.gouv.etalab.mastodon.client.Entities.Account;
import fr.gouv.etalab.mastodon.client.Entities.Error;
import fr.gouv.etalab.mastodon.client.Entities.Relationship;
import fr.gouv.etalab.mastodon.drawers.AccountSearchDevAdapter;
import fr.gouv.etalab.mastodon.helper.ExpandableHeightListView;
import fr.gouv.etalab.mastodon.helper.Helper;
import fr.gouv.etalab.mastodon.interfaces.OnRetrieveSearcAccountshInterface;
import fr.gouv.etalab.mastodon.interfaces.OnRetrieveRelationshipInterface;
import fr.gouv.etalab.mastodon.interfaces.OnRetrieveRemoteAccountInterface;
import fr.gouv.etalab.mastodon.interfaces.OnRetrieveSearchDevelopersAccountshInterface;
import mastodon.etalab.gouv.fr.mastodon.R;
@ -48,9 +52,12 @@ import mastodon.etalab.gouv.fr.mastodon.R;
* About activity
*/
public class AboutActivity extends AppCompatActivity implements OnRetrieveSearcAccountshInterface {
public class AboutActivity extends AppCompatActivity implements OnRetrieveRemoteAccountInterface, OnRetrieveSearchDevelopersAccountshInterface, OnRetrieveRelationshipInterface {
private Button about_developer;
private List<Account> developers = new ArrayList<>();
private List<Account> 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<Account> 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<Account> 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;
}
}
}
}

View File

@ -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 <http://www.gnu.org/licenses>. */
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);
}
}

View File

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

View File

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

View File

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

View File

@ -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> 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<Account>());
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 "<space>dot<space>" 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);
}
}
}

View File

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

View File

@ -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<Void, Void, Void> {
private Context context;
private APIResponse apiResponse;
private OnRetrieveSearcAccountshInterface listener;
private OnRetrieveSearchDevelopersAccountshInterface listener;
private ArrayList<Account> 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);
}
}

View File

@ -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<Void, Void, Void> {
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<Void, Void, Void> {
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<Void, Void, Void> {
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<Void, Void, Void> {
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();
}
}
}

View File

@ -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 <http://www.gnu.org/licenses>. */
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<Void, Void, Void> {
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);
}
}

View File

@ -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<Void, Void, Void>
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<Void, Void, Void>
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();
}
}

View File

@ -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<Void, Void, Void> {
@Override
protected Void doInBackground(Void... params) {
api = new API(context);
relationship = api.getRelationship(accountId);
return null;

View File

@ -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<Void, Void, Void>
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<Void, Void, Void>
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);
}
}

View File

@ -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<Void, Void, Void>
}else{
jobIds = new int[]{};
}
if( storedStatuses != null && storedStatuses.size() > 0 ){
for(StoredStatus ss: storedStatuses){
if (!Helper.isJobPresent(jobIds, ss.getJobId())){

View File

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

View File

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

View File

@ -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<Notification> CREATOR = new Creator<Notification>() {
@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);
}
}

View File

@ -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<Status> replies) {
this.replies = replies;
}
public boolean isNew() {
return isNew;
}
public void setNew(boolean aNew) {
isNew = aNew;
}
}

View File

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

View File

@ -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 <http://www.gnu.org/licenses>. */
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<Account> accounts;
private LayoutInflater layoutInflater;
private Context context;
private ViewHolder holder;
public AccountSearchDevAdapter(Context context, List<Account> 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;
}
}

View File

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

View File

@ -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<Notification> 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("</p>","<br/><br/>");
content = content.replaceAll("<p>","");
if( content.endsWith("<br/><br/>") )
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() {

View File

@ -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 <http://www.gnu.org/licenses>. */
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("</p>","<br/><br/>");
content = content.replaceAll("<p>","");
if( content.endsWith("<br/><br/>") )
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("&amp;", "&");
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;
}

View File

@ -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<String> 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<Notification> 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<Notification> 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<String> 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<String> 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);
}
}

View File

@ -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<String> 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<Status> 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<Status> 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<String> 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<String> 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();
}
}
}

View File

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

View File

@ -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<CharSequence> 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));

View File

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

View File

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

View File

@ -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<ArrayList<Notification>>() {}.getType();
ArrayList<Notification> 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<ArrayList<Status>>() {}.getType();
ArrayList<Status> 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<Status> 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<Status> 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<Status> 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<ArrayList<Status>>() {}.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<Notification> 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<Notification> 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<Notification> 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<ArrayList<Notification>>() {}.getType();
return gson.fromJson(json, type);
}
}

View File

@ -0,0 +1,9 @@
package fr.gouv.etalab.mastodon.helper;
/**
* Created by Thomas on 02/09/2017.
*/
public class ManageHeader {
}

View File

@ -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 <http://www.gnu.org/licenses>. */
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);
}

View File

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

View File

@ -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 <http://www.gnu.org/licenses>. */
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<Account> accounts);
}

View File

@ -127,24 +127,17 @@ public class HomeTimelineSyncJob extends Job implements OnRetrieveHomeTimelineSe
@Override
public void onRetrieveHomeTimelineService(APIResponse apiResponse, String acct, String userId) {
List<Status> statuses = apiResponse.getStatuses();
public void onRetrieveHomeTimelineService(APIResponse apiResponse, String acct, final String userId) {
final List<Status> 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
}
}
}

View File

@ -133,8 +133,8 @@ public class NotificationsSyncJob extends Job implements OnRetrieveNotifications
@Override
public void onRetrieveNotifications(APIResponse apiResponse, String acct, String userId, boolean refreshData) {
List<Notification> notifications = apiResponse.getNotifications();
public void onRetrieveNotifications(APIResponse apiResponse, String acct, final String userId, boolean refreshData) {
final List<Notification> 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();
}
}});
}
}
}
}
}

View File

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

View File

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

View File

@ -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 <http://www.gnu.org/licenses>. */
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<String, HttpsURLConnection> connectionHashMap = new HashMap<>();
private static HashMap<String, Boolean> 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<Account> 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<Status>()); //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<Account> 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());
}
}

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 939 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 939 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 514 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 514 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 556 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 556 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 606 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 606 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 602 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 602 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 502 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 490 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1008 B

After

Width:  |  Height:  |  Size: 1008 B

View File

@ -16,9 +16,9 @@
see <http://www.gnu.org/licenses>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<!-- About app name -->
<TextView
@ -40,40 +40,49 @@
android:layout_height="wrap_content" />
<!-- About developer -->
<LinearLayout
android:orientation="horizontal"
android:padding="20dp"
android:gravity="center"
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|center_horizontal"
android:layout_marginTop="10dp"
android:clickable="true"
android:gravity="center_vertical"
android:padding="5dp"
android:text="@string/about_developer"
android:textColor="@color/mastodonC4"
android:textSize="16sp"/>
<fr.gouv.etalab.mastodon.helper.ExpandableHeightListView
android:id="@+id/lv_developers"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:textSize="18sp"
android:layout_gravity="center"
android:gravity="center"
android:clickable="true"
android:layout_width="200dp"
android:text="@string/about_developer"
android:layout_height="wrap_content" />
<Button
android:id="@+id/about_developer"
android:text="@string/about_developer_action"
android:layout_width="150dp"
style="@style/Base.Widget.AppCompat.Button.Colored"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:padding="10dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|center_horizontal"
android:layout_marginLeft="150dp"
android:layout_marginRight="150dp"
android:divider="@null"
android:scrollbars="none"/>
<TextView
android:id="@+id/about_thanks_dev"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|center_horizontal"
android:layout_marginTop="10dp"
android:gravity="center_vertical"
android:padding="5dp"
android:text="@string/thanks_text_dev"
android:textColor="@color/mastodonC4"
android:textSize="16sp"/>
<fr.gouv.etalab.mastodon.helper.ExpandableHeightListView
android:id="@+id/lv_contributors"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/about_thanks_dev"
android:text="@string/thanks_text_dev"
android:layout_marginTop="20dp"
android:gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|center_horizontal"
android:layout_marginLeft="150dp"
android:layout_marginRight="150dp"
android:divider="@null"
android:scrollbars="none"/>
<!-- About license -->
<LinearLayout
@ -89,12 +98,13 @@
android:gravity="center"
android:layout_width="200dp"
android:layout_height="wrap_content" />
<Button
android:id="@+id/about_license"
android:text="@string/about_license_action"
style="@style/Base.Widget.AppCompat.Button.Colored"
android:layout_width="150dp"
android:layout_height="wrap_content" />
android:layout_width="165dp"
android:layout_height="wrap_content"
android:text="@string/about_license_action"/>
</LinearLayout>
<!-- About license -->
@ -111,12 +121,13 @@
android:gravity="center"
android:layout_width="200dp"
android:layout_height="wrap_content" />
<Button
android:id="@+id/about_code"
android:text="@string/about_code_action"
style="@style/Base.Widget.AppCompat.Button.Colored"
android:layout_width="150dp"
android:layout_height="wrap_content" />
android:layout_width="165dp"
android:layout_height="wrap_content"
android:text="@string/about_code_action"/>
</LinearLayout>
@ -136,12 +147,13 @@
android:layout_width="200dp"
android:text="@string/about_thekinrar"
android:layout_height="wrap_content" />
<Button
android:id="@+id/about_thekinrar"
android:text="@string/about_thekinrar_action"
android:layout_width="150dp"
style="@style/Base.Widget.AppCompat.Button.Colored"
android:layout_height="wrap_content" />
android:layout_width="165dp"
android:layout_height="wrap_content"
android:text="@string/about_thekinrar_action"/>
</LinearLayout>
<!-- About translation -->
@ -158,12 +170,13 @@
android:gravity="center"
android:layout_width="200dp"
android:layout_height="wrap_content" />
<Button
android:id="@+id/about_translation"
android:text="@string/about_yandex_action"
style="@style/Base.Widget.AppCompat.Button.Colored"
android:layout_width="150dp"
android:layout_height="wrap_content" />
android:layout_width="165dp"
android:layout_height="wrap_content"
android:text="@string/about_yandex_action"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"

View File

@ -68,16 +68,32 @@
android:maxLines="1"
android:textSize="16sp"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
<TextView
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:textColor="?attr/color_in_account_header"
android:id="@+id/account_un"
android:maxLines="1"
<LinearLayout
android:layout_width="wrap_content"
android:textSize="14sp"
android:layout_height="wrap_content"
/>
android:orientation="horizontal">
<TextView
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:textColor="?attr/color_in_account_header"
android:id="@+id/account_un"
android:maxLines="1"
android:layout_width="wrap_content"
android:textSize="14sp"
android:layout_height="wrap_content"
/>
<ImageView
android:visibility="gone"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:gravity="center_horizontal|bottom"
android:src="@drawable/ic_action_edit"
android:id="@+id/header_edit_profile"
android:layout_width="20dp"
android:layout_height="20dp"
tools:ignore="ContentDescription" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@ -40,6 +40,12 @@
android:text="@string/set_share_validation"
android:layout_height="wrap_content" />
<CheckBox
android:id="@+id/set_share_validation_fav"
android:layout_width="wrap_content"
android:text="@string/set_share_validation_fav"
android:layout_height="wrap_content" />
<CheckBox
android:id="@+id/set_show_error_messages"
android:layout_width="wrap_content"
@ -58,11 +64,6 @@
android:text="@string/set_auto_store_toot"
android:layout_height="wrap_content" />
<CheckBox
android:id="@+id/set_bubble_counter"
android:layout_width="wrap_content"
android:text="@string/set_bubble_counter"
android:layout_height="wrap_content" />
<CheckBox
android:id="@+id/set_preview_reply"
@ -70,6 +71,17 @@
android:text="@string/set_preview_reply"
android:layout_height="wrap_content" />
<CheckBox
android:id="@+id/set_display_local"
android:layout_width="wrap_content"
android:text="@string/set_display_local"
android:layout_height="wrap_content" />
<CheckBox
android:id="@+id/set_display_global"
android:layout_width="wrap_content"
android:text="@string/set_display_global"
android:layout_height="wrap_content" />
<LinearLayout
android:id="@+id/set_preview_reply_pp_container"
@ -94,6 +106,24 @@
/>
</LinearLayout>
<!-- TABS Layout -->
<LinearLayout
android:id="@+id/translation_layout_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/about_yandex"/>
<Spinner
android:id="@+id/translation_layout_spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
@ -138,25 +168,7 @@
android:layout_height="wrap_content" />
<!-- TABS Layout -->
<LinearLayout
android:id="@+id/translation_layout_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:layout_gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/about_yandex"/>
<Spinner
android:id="@+id/translation_layout_spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:id="@+id/set_javascript_container"

View File

@ -15,172 +15,188 @@
You should have received a copy of the GNU General Public License along with Mastalab; if not,
see <http://www.gnu.org/licenses>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<!-- About app name -->
<TextView
android:layout_marginTop="20dp"
android:textSize="20sp"
android:layout_gravity="center_horizontal"
android:gravity="center_horizontal"
android:text="@string/app_name"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<!-- About version -->
<TextView
android:layout_marginTop="10dp"
android:id="@+id/about_version"
android:textSize="16sp"
android:layout_gravity="center_horizontal"
android:gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<!-- About developer -->
<LinearLayout
android:orientation="horizontal"
android:padding="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:orientation="vertical"
>
<!-- About app name -->
<TextView
android:layout_marginTop="10dp"
android:textSize="20sp"
android:layout_gravity="center_horizontal"
android:gravity="center_horizontal"
android:text="@string/app_name"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<!-- About version -->
<TextView
android:layout_marginTop="10dp"
android:id="@+id/about_version"
android:textSize="16sp"
android:layout_gravity="center"
android:gravity="center"
android:layout_gravity="center_horizontal"
android:gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<!-- About developer -->
<TextView
android:padding="5dp"
android:layout_marginTop="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:clickable="true"
android:layout_weight="2"
android:layout_width="0dp"
android:textColor="@color/mastodonC4"
android:gravity="center_vertical"
android:text="@string/about_developer"
android:layout_height="wrap_content" />
<Button
android:id="@+id/about_developer"
android:text="@string/about_developer_action"
android:layout_weight="3"
android:layout_width="0dp"
style="@style/Base.Widget.AppCompat.Button.Colored"
android:layout_height="wrap_content" />
</LinearLayout>
android:textSize="16sp"/>
<fr.gouv.etalab.mastodon.helper.ExpandableHeightListView
android:id="@+id/lv_developers"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none"
android:divider="@null"/>
<LinearLayout
android:orientation="horizontal"
android:padding="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:padding="5dp"
android:layout_marginTop="10dp"
android:id="@+id/about_thanks_dev"
android:text="@string/thanks_text_dev"
android:layout_marginTop="20dp"
android:gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<!-- About license -->
<LinearLayout
android:orientation="horizontal"
android:padding="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:text="@string/about_license"
android:textColor="@color/mastodonC4"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:textSize="16sp"
android:layout_gravity="center"
android:gravity="center"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="wrap_content" />
<Button
android:id="@+id/about_license"
android:text="@string/about_license_action"
style="@style/Base.Widget.AppCompat.Button.Colored"
android:layout_weight="3"
android:layout_width="0dp"
android:layout_height="wrap_content" />
</LinearLayout>
android:text="@string/thanks_text_dev"/>
<!-- About license -->
<LinearLayout
android:orientation="horizontal"
android:padding="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:text="@string/about_code"
android:textSize="16sp"
android:layout_gravity="center"
android:gravity="center"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="wrap_content" />
<Button
android:id="@+id/about_code"
android:text="@string/about_code_action"
style="@style/Base.Widget.AppCompat.Button.Colored"
android:layout_weight="3"
android:layout_width="0dp"
android:layout_height="wrap_content" />
</LinearLayout>
<!-- About The Kinrar's API -->
<LinearLayout
android:orientation="horizontal"
android:padding="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:textSize="16sp"
android:layout_gravity="center"
android:gravity="center"
android:clickable="true"
android:layout_weight="2"
android:layout_width="0dp"
android:text="@string/about_thekinrar"
android:layout_height="wrap_content" />
<Button
android:id="@+id/about_thekinrar"
android:text="@string/about_thekinrar_action"
android:layout_weight="3"
android:layout_width="0dp"
style="@style/Base.Widget.AppCompat.Button.Colored"
android:layout_height="wrap_content" />
</LinearLayout>
<!-- About translation -->
<LinearLayout
android:orientation="horizontal"
android:padding="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:text="@string/about_yandex"
android:textSize="16sp"
android:layout_gravity="center"
android:gravity="center"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="wrap_content" />
<Button
android:id="@+id/about_translation"
android:text="@string/about_yandex_action"
style="@style/Base.Widget.AppCompat.Button.Colored"
android:layout_weight="3"
android:layout_width="0dp"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:padding="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:text="@string/thanks_text"
android:layout_marginTop="20dp"
android:gravity="center_horizontal"
<fr.gouv.etalab.mastodon.helper.ExpandableHeightListView
android:id="@+id/lv_contributors"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
android:layout_height="wrap_content"
android:scrollbars="none"
android:divider="@null"/>
<!-- About license -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="5dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="2"
android:gravity="center"
android:text="@string/about_license"
android:textSize="16sp"/>
<Button
android:id="@+id/about_license"
style="@style/Base.Widget.AppCompat.Button.Colored"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:text="@string/about_license_action"/>
</LinearLayout>
<!-- About license -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="5dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="2"
android:gravity="center"
android:text="@string/about_code"
android:textSize="16sp"/>
<Button
android:id="@+id/about_code"
style="@style/Base.Widget.AppCompat.Button.Colored"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:text="@string/about_code_action"/>
</LinearLayout>
<!-- About The Kinrar's API -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="5dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="2"
android:clickable="true"
android:gravity="center"
android:text="@string/about_thekinrar"
android:textSize="16sp"/>
<Button
android:id="@+id/about_thekinrar"
style="@style/Base.Widget.AppCompat.Button.Colored"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:text="@string/about_thekinrar_action"/>
</LinearLayout>
<!-- About translation -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="5dp">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_weight="2"
android:gravity="center"
android:text="@string/about_yandex"
android:textSize="16sp"/>
<Button
android:id="@+id/about_translation"
style="@style/Base.Widget.AppCompat.Button.Colored"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:text="@string/about_yandex_action"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:layout_marginTop="10dp"
android:orientation="horizontal"
android:padding="10dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:text="@string/thanks_text"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>

View File

@ -68,16 +68,32 @@
android:maxLines="1"
android:textSize="16sp"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
<TextView
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:textColor="?attr/color_in_account_header"
android:id="@+id/account_un"
android:maxLines="1"
<LinearLayout
android:layout_width="wrap_content"
android:textSize="14sp"
android:layout_height="wrap_content"
/>
android:orientation="horizontal">
<TextView
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:textColor="?attr/color_in_account_header"
android:id="@+id/account_un"
android:maxLines="1"
android:layout_width="wrap_content"
android:textSize="14sp"
android:layout_height="wrap_content"
/>
<ImageView
android:visibility="gone"
android:layout_marginLeft="20dp"
android:layout_marginStart="20dp"
android:gravity="center_horizontal|bottom"
android:src="@drawable/ic_action_edit"
android:id="@+id/header_edit_profile"
android:layout_width="20dp"
android:layout_height="20dp"
tools:ignore="ContentDescription" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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 <http://www.gnu.org/licenses>.
-->
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:card_view="http://schemas.android.com/tools"
style="?attr/cardStyle"
android:layout_marginTop="10dp"
card_view:cardPreventCornerOverlap="true"
app:cardUseCompatPadding="true">
<LinearLayout
android:id="@+id/acccount_container"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
tools:ignore="UseCompoundDrawables">
<ImageView
android:layout_gravity="center_horizontal"
android:id="@+id/account_pp"
android:layout_margin="10dp"
android:layout_width="60dp"
android:layout_height="60dp"
tools:ignore="ContentDescription" />
<LinearLayout
android:layout_marginTop="10dp"
android:layout_marginStart="10dp"
android:layout_marginLeft="10dp"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/account_dn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
<TextView
android:id="@+id/account_un"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
</LinearLayout>
<android.support.design.widget.FloatingActionButton
android:layout_gravity="center"
app:fabSize="mini"
android:id="@+id/account_follow"
android:textAllCaps="false"
android:src="@drawable/ic_user_plus"
android:gravity="center"
android:layout_marginRight="10dp"
android:layout_marginEnd="10dp"
android:layout_width="40dp"
android:layout_height="40dp"
/>
</LinearLayout>
</android.support.v7.widget.CardView>

View File

@ -35,7 +35,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/main_container"
android:orientation="horizontal">
android:orientation="horizontal"
card_view:ignore="DisableBaselineAlignment">
<LinearLayout
android:layout_width="60dp"
android:layout_height="wrap_content"

View File

@ -71,21 +71,15 @@
style="?attr/shapeBorder"
android:visibility="gone"
tools:ignore="ContentDescription" />
<Button
android:id="@+id/status_translate"
android:layout_width="50dp"
android:layout_height="wrap_content"
<ImageView
android:layout_centerHorizontal="true"
android:layout_marginTop="70dp"
android:layout_marginLeft="5dp"
android:layout_marginStart="5dp"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:scaleType="center"
android:drawablePadding="0dp"
android:paddingStart="17dp"
android:paddingLeft="17dp"
android:drawableStart="@drawable/ic_translate"
android:drawableLeft="@drawable/ic_translate"
android:id="@+id/new_element"
android:visibility="gone"
android:src="@drawable/ic_fiber_new"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:ignore="ContentDescription" />
</RelativeLayout>
<LinearLayout
@ -196,6 +190,14 @@
android:text="Powered by Yandex.Translate"
android:layout_height="wrap_content"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/google_translate"
android:layout_width="match_parent"
android:padding="2dp"
android:gravity="end"
android:text="Powered by Google Translate"
android:layout_height="wrap_content"
tools:ignore="HardcodedText" />
</LinearLayout>
<LinearLayout
@ -330,11 +332,22 @@
</LinearLayout>
</LinearLayout>
<TextView
android:visibility="gone"
android:text="@string/translate_toot"
android:layout_marginEnd="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="10dp"
android:gravity="center_vertical|end"
android:id="@+id/status_translate"
android:textColor="@color/mastodonC4"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<LinearLayout
android:id="@+id/status_action_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginTop="5dp"
android:padding="@dimen/drawer_padding"
android:layout_marginStart="@dimen/activity_vertical_margin"
android:layout_marginLeft="@dimen/activity_vertical_margin"

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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 <http://www.gnu.org/licenses>.
-->
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
app:popupTheme="?attr/popupOverlay">
<ImageView
android:id="@+id/close_conversation"
android:src="@drawable/ic_close"
android:layout_width="30dp"
android:layout_height="30dp"
android:gravity="center_vertical"
android:layout_marginEnd="10dp"
android:layout_marginRight="10dp"
tools:ignore="ContentDescription" />
<ImageView
android:id="@+id/pp_actionBar"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_gravity="center_vertical"
android:gravity="center_vertical"
android:layout_marginEnd="10dp"
android:layout_marginRight="10dp"
tools:ignore="ContentDescription" />
<TextView
android:layout_width="wrap_content"
android:layout_gravity="center_vertical"
android:layout_height="wrap_content"
android:textColor="?attr/actionBarTextColor"
android:textSize="14sp"
android:id="@+id/toolbar_title" />
</android.support.v7.widget.Toolbar>

View File

@ -40,6 +40,12 @@
android:text="@string/set_share_validation"
android:layout_height="wrap_content" />
<CheckBox
android:id="@+id/set_share_validation_fav"
android:layout_width="wrap_content"
android:text="@string/set_share_validation_fav"
android:layout_height="wrap_content" />
<CheckBox
android:id="@+id/set_show_error_messages"
android:layout_width="wrap_content"
@ -58,11 +64,6 @@
android:text="@string/set_auto_store_toot"
android:layout_height="wrap_content" />
<CheckBox
android:id="@+id/set_bubble_counter"
android:layout_width="wrap_content"
android:text="@string/set_bubble_counter"
android:layout_height="wrap_content" />
<CheckBox
android:id="@+id/set_preview_reply"
@ -70,6 +71,18 @@
android:text="@string/set_preview_reply"
android:layout_height="wrap_content" />
<CheckBox
android:id="@+id/set_display_local"
android:layout_width="wrap_content"
android:text="@string/set_display_local"
android:layout_height="wrap_content" />
<CheckBox
android:id="@+id/set_display_global"
android:layout_width="wrap_content"
android:text="@string/set_display_global"
android:layout_height="wrap_content" />
<LinearLayout
android:id="@+id/set_preview_reply_pp_container"
android:layout_width="match_parent"
@ -93,7 +106,24 @@
/>
</LinearLayout>
<!-- Translation engine -->
<LinearLayout
android:layout_marginTop="10dp"
android:id="@+id/translation_layout_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/about_yandex"/>
<Spinner
android:id="@+id/translation_layout_spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
@ -139,25 +169,7 @@
<!-- Translation engine -->
<LinearLayout
android:layout_marginTop="10dp"
android:layout_marginBottom="20dp"
android:id="@+id/translation_layout_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:layout_gravity="center_vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/about_yandex"/>
<Spinner
android:id="@+id/translation_layout_spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:id="@+id/set_javascript_container"

View File

@ -173,6 +173,25 @@
android:layout_height="wrap_content"
/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:id="@+id/set_led_colour_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10dp"
android:text="@string/set_led_colour"/>
<Spinner
android:id="@+id/led_colour_spinner"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:entries="@array/led_colours"/>
</LinearLayout>
</LinearLayout>
</ScrollView>

Some files were not shown because too many files have changed in this diff Show More