Merged in develop (pull request #13)

This commit is contained in:
tom79 2017-06-10 14:21:34 +00:00
commit 1bbc5c2c86
48 changed files with 990 additions and 141 deletions

View File

@ -7,8 +7,8 @@ android {
applicationId "fr.gouv.etalab.mastodon"
minSdkVersion 15
targetSdkVersion 25
versionCode 15
versionName "1.1.8"
versionCode 16
versionName "1.1.9"
}
buildTypes {
release {
@ -26,4 +26,7 @@ dependencies {
compile 'com.loopj.android:android-async-http:1.4.9'
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
compile 'com.evernote:android-job:1.1.10'
compile ('com.vdurmont:emoji-java:3.2.0') {
exclude group: 'org.json'
}
}

Binary file not shown.

View File

@ -41,6 +41,11 @@
android:windowSoftInputMode="stateAlwaysHidden"
android:configChanges="keyboardHidden|orientation|screenSize"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
<activity android:name="fr.gouv.etalab.mastodon.activities.LoginActivity"
android:windowSoftInputMode="stateAlwaysHidden"

View File

@ -24,6 +24,7 @@ import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.FragmentManager;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.support.design.widget.NavigationView;
import android.support.v4.view.GravityCompat;
@ -50,6 +51,7 @@ import fr.gouv.etalab.mastodon.asynctasks.UpdateAccountInfoByIDAsyncTask;
import fr.gouv.etalab.mastodon.client.Entities.Account;
import fr.gouv.etalab.mastodon.client.PatchBaseImageDownloader;
import fr.gouv.etalab.mastodon.fragments.DisplayAccountsFragment;
import fr.gouv.etalab.mastodon.fragments.DisplayFollowRequestSentFragment;
import fr.gouv.etalab.mastodon.fragments.DisplayNotificationsFragment;
import fr.gouv.etalab.mastodon.helper.Helper;
import fr.gouv.etalab.mastodon.interfaces.OnUpdateAccountInfoInterface;
@ -80,7 +82,15 @@ public class MainActivity extends AppCompatActivity
private ImageLoader imageLoader;
private DisplayImageOptions options;
private View headerLayout;
static final int MIN_DISTANCE = 150;
private float downX, downY;
private int currentScreen = 1;
private actionSwipe currentAction;
private enum actionSwipe{
RIGHT_TO_LEFT,
LEFT_TO_RIGHT
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -140,6 +150,13 @@ public class MainActivity extends AppCompatActivity
Account account = new AccountDAO(getApplicationContext(), db).getAccountByToken(prefKeyOauthTokenT);
updateHeaderAccountInfo(MainActivity.this, account, headerLayout, imageLoader, options);
//Locked account can see follow request
if (account.isLocked()) {
navigationView.getMenu().findItem(R.id.nav_follow_request).setVisible(true);
} else {
navigationView.getMenu().findItem(R.id.nav_follow_request).setVisible(false);
}
LinearLayout owner_container = (LinearLayout) headerLayout.findViewById(R.id.owner_container);
owner_container.setOnClickListener(new View.OnClickListener() {
@Override
@ -147,7 +164,6 @@ public class MainActivity extends AppCompatActivity
menuAccounts(MainActivity.this);
}
});
boolean matchingIntent = mamageNewIntent(getIntent());
if (savedInstanceState == null && !matchingIntent) {
navigationView.setCheckedItem(R.id.nav_home);
@ -202,6 +218,8 @@ public class MainActivity extends AppCompatActivity
private boolean mamageNewIntent(Intent intent){
if( intent == null || intent.getExtras() == null )
return false;
String action = intent.getAction();
String type = intent.getType();
Bundle extras = intent.getExtras();
String userIdIntent;
boolean matchingIntent = false;
@ -227,6 +245,17 @@ public class MainActivity extends AppCompatActivity
navigationView.getMenu().findItem(R.id.nav_home).setChecked(true);
matchingIntent = true;
}
}else if( Intent.ACTION_SEND.equals(action) && type != null ){
if ("text/plain".equals(type)) {
String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT);
if (sharedText != null) {
Intent intentToot = new Intent(getApplicationContext(), TootActivity.class);
Bundle b = new Bundle();
b.putString("sharedContent", sharedText);
intentToot.putExtras(b);
startActivity(intentToot);
}
}
}
intent.replaceExtras(new Bundle());
intent.setAction("");
@ -387,18 +416,28 @@ public class MainActivity extends AppCompatActivity
Bundle bundle = new Bundle();
FragmentManager fragmentManager = getSupportFragmentManager();
String fragmentTag = null;
currentScreen = -1;
if (id == R.id.nav_home) {
toot.setVisibility(View.VISIBLE);
statusFragment = new DisplayStatusFragment();
bundle.putSerializable("type", RetrieveFeedsAsyncTask.Type.HOME);
statusFragment.setArguments(bundle);
fragmentTag = "HOME_TIMELINE";
if(! first)
fragmentManager.beginTransaction()
.replace(R.id.main_app_container, statusFragment, fragmentTag).addToBackStack(fragmentTag).commit();
else{
fragmentManager.beginTransaction()
currentScreen = 1;
if(! first) {
if( currentAction == actionSwipe.RIGHT_TO_LEFT)
fragmentManager.beginTransaction().setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right)
.replace(R.id.main_app_container, statusFragment, fragmentTag).addToBackStack(fragmentTag).commit();
else
fragmentManager.beginTransaction().setCustomAnimations(R.anim.enter_from_left, R.anim.exit_to_right, R.anim.enter_from_right, R.anim.exit_to_left)
.replace(R.id.main_app_container, statusFragment, fragmentTag).addToBackStack(fragmentTag).commit();
}else{
if( currentAction == actionSwipe.RIGHT_TO_LEFT)
fragmentManager.beginTransaction().setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right)
.replace(R.id.main_app_container, statusFragment, fragmentTag).commit();
else
fragmentManager.beginTransaction().setCustomAnimations(R.anim.enter_from_left, R.anim.exit_to_right, R.anim.enter_from_right, R.anim.exit_to_left)
.replace(R.id.main_app_container, statusFragment, fragmentTag).commit();
first = false;
}
} else if (id == R.id.nav_local) {
@ -407,8 +446,13 @@ public class MainActivity extends AppCompatActivity
bundle.putSerializable("type", RetrieveFeedsAsyncTask.Type.LOCAL);
statusFragment.setArguments(bundle);
fragmentTag = "LOCAL_TIMELINE";
fragmentManager.beginTransaction()
currentScreen = 2;
if( currentAction == actionSwipe.RIGHT_TO_LEFT)
fragmentManager.beginTransaction().setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right)
.replace(R.id.main_app_container, statusFragment, fragmentTag).addToBackStack(fragmentTag).commit();
else
fragmentManager.beginTransaction().setCustomAnimations(R.anim.enter_from_left, R.anim.exit_to_right, R.anim.enter_from_right, R.anim.exit_to_left)
.replace(R.id.main_app_container, statusFragment, fragmentTag).addToBackStack(fragmentTag).commit();
} else if (id == R.id.nav_global) {
toot.setVisibility(View.VISIBLE);
@ -416,8 +460,13 @@ public class MainActivity extends AppCompatActivity
bundle.putSerializable("type", RetrieveFeedsAsyncTask.Type.PUBLIC);
statusFragment.setArguments(bundle);
fragmentTag = "PUBLIC_TIMELINE";
fragmentManager.beginTransaction()
currentScreen = 3;
if( currentAction == actionSwipe.RIGHT_TO_LEFT)
fragmentManager.beginTransaction().setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left, R.anim.enter_from_left, R.anim.exit_to_right)
.replace(R.id.main_app_container, statusFragment, fragmentTag).addToBackStack(fragmentTag).commit();
else
fragmentManager.beginTransaction().setCustomAnimations(R.anim.enter_from_left, R.anim.exit_to_right, R.anim.enter_from_right, R.anim.exit_to_left)
.replace(R.id.main_app_container, statusFragment, fragmentTag).addToBackStack(fragmentTag).commit();
} else if (id == R.id.nav_settings) {
toot.setVisibility(View.GONE);
TabLayoutSettingsFragment tabLayoutSettingsFragment= new TabLayoutSettingsFragment();
@ -455,9 +504,13 @@ public class MainActivity extends AppCompatActivity
fragmentTag = "NOTIFICATIONS";
fragmentManager.beginTransaction()
.replace(R.id.main_app_container, notificationsFragment, fragmentTag).addToBackStack(fragmentTag).commit();
}else if( id == R.id.nav_follow_request){
toot.setVisibility(View.GONE);
DisplayFollowRequestSentFragment followRequestSentFragment = new DisplayFollowRequestSentFragment();
fragmentTag = "FOLLOW_REQUEST_SENT";
fragmentManager.beginTransaction()
.replace(R.id.main_app_container, followRequestSentFragment, fragmentTag).addToBackStack(fragmentTag).commit();
}
setTitle(item.getTitle());
populateTitleWithTag(fragmentTag, item.getTitle().toString(), item.getItemId());
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
@ -497,4 +550,68 @@ public class MainActivity extends AppCompatActivity
updateHeaderAccountInfo(MainActivity.this, account, headerLayout, imageLoader, options);
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
//Default dispatchTouchEvent is returned when not in timeline page
if( currentScreen >3 || currentScreen < 1)
return super.dispatchTouchEvent(event);
switch(event.getAction()){
case MotionEvent.ACTION_DOWN: {
downX = event.getX();
downY = event.getY();
return super.dispatchTouchEvent(event);
}
case MotionEvent.ACTION_UP: {
float upX = event.getX();
float upY = event.getY();
float deltaX = downX - upX;
float deltaY = downY - upY;
// swipe horizontal
if(Math.abs(deltaX) > MIN_DISTANCE && Math.abs(deltaY) < MIN_DISTANCE){
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
if(deltaX < 0) { switchOnSwipe(actionSwipe.LEFT_TO_RIGHT); drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);return true; }
if(deltaX > 0) { switchOnSwipe(actionSwipe.RIGHT_TO_LEFT); drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);return true; }
}
}
}
return super.dispatchTouchEvent(event);
}
private void switchOnSwipe(actionSwipe action){
currentScreen = (action == actionSwipe.LEFT_TO_RIGHT)?currentScreen-1:currentScreen+1;
if( currentScreen > 3 )
currentScreen = 1;
if( currentScreen < 1)
currentScreen = 3;
currentAction = action;
final NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
switch (currentScreen){
case 1:
unCheckAllMenuItems(navigationView.getMenu());
navigationView.getMenu().performIdentifierAction(R.id.nav_home, 0);
if( navigationView.getMenu().findItem(R.id.nav_home) != null)
navigationView.getMenu().findItem(R.id.nav_home).setChecked(true);
break;
case 2:
unCheckAllMenuItems(navigationView.getMenu());
navigationView.getMenu().performIdentifierAction(R.id.nav_local, 0);
if( navigationView.getMenu().findItem(R.id.nav_local) != null)
navigationView.getMenu().findItem(R.id.nav_local).setChecked(true);
break;
case 3:
unCheckAllMenuItems(navigationView.getMenu());
navigationView.getMenu().performIdentifierAction(R.id.nav_global, 0);
if( navigationView.getMenu().findItem(R.id.nav_global) != null)
navigationView.getMenu().findItem(R.id.nav_global).setChecked(true);
break;
default:
break;
}
}
}

View File

@ -63,9 +63,9 @@ public class SearchResultActivity extends AppCompatActivity implements OnRetriev
if( search != null)
new RetrieveSearchAsyncTask(getApplicationContext(), search.trim(), SearchResultActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
else
Toast.makeText(this,R.string.toast_error_loading_account,Toast.LENGTH_LONG).show();
Toast.makeText(this,R.string.toast_error_search,Toast.LENGTH_LONG).show();
}else{
Toast.makeText(this,R.string.toast_error_loading_account,Toast.LENGTH_LONG).show();
Toast.makeText(this,R.string.toast_error_search,Toast.LENGTH_LONG).show();
}
if( getSupportActionBar() != null)
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
@ -99,7 +99,7 @@ public class SearchResultActivity extends AppCompatActivity implements OnRetriev
Toast.makeText(getApplicationContext(), error.getError(),Toast.LENGTH_LONG).show();
return;
}
if( results == null){
if( results == null || (results.getAccounts().size() == 0 && results.getStatuses().size() == 0 && results.getHashtags().size() == 0)){
RelativeLayout no_result = (RelativeLayout) findViewById(R.id.no_result);
no_result.setVisibility(View.VISIBLE);
return;

View File

@ -19,6 +19,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.graphics.PorterDuff;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
@ -27,6 +28,7 @@ import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
@ -43,6 +45,7 @@ import android.widget.Toast;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer;
import com.vdurmont.emoji.EmojiParser;
import java.util.ArrayList;
import java.util.List;
@ -223,7 +226,9 @@ public class ShowAccountActivity extends AppCompatActivity implements OnPostActi
account_follow.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if( doAction == action.FOLLOW){
if( doAction == action.NOTHING){
Toast.makeText(getApplicationContext(), R.string.nothing_to_do, Toast.LENGTH_LONG).show();
}else if( doAction == action.FOLLOW){
account_follow.setEnabled(false);
new PostActionAsyncTask(getApplicationContext(), API.StatusAction.FOLLOW, accountId, ShowAccountActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}else if( doAction == action.UNFOLLOW){
@ -278,8 +283,8 @@ public class ShowAccountActivity extends AppCompatActivity implements OnPostActi
if( account != null){
setTitle(account.getAcct());
account_dn.setText(account.getDisplay_name());
account_un.setText(account.getUsername());
account_dn.setText(EmojiParser.parseToUnicode(account.getDisplay_name()));
account_un.setText(String.format("@%s", account.getUsername()));
if( account.getAcct() != null && account.getAcct().equals(account.getUsername()))
account_ac.setVisibility(View.GONE);
else
@ -322,11 +327,13 @@ public class ShowAccountActivity extends AppCompatActivity implements OnPostActi
Toast.makeText(getApplicationContext(), error.getError(),Toast.LENGTH_LONG).show();
return;
}
account_follow.setEnabled(true);
if( relationship.isBlocking()){
account_follow.setText(R.string.action_unblock);
doAction = action.UNBLOCK;
}else if( relationship.isRequested()){
account_follow.setText(R.string.request_sent);
account_follow.getBackground().setColorFilter(ContextCompat.getColor(this, R.color.colorPrimary), PorterDuff.Mode.MULTIPLY);
doAction = action.NOTHING;
}else if( relationship.isFollowing()){
account_follow.setText(R.string.action_unfollow);
@ -335,13 +342,12 @@ public class ShowAccountActivity extends AppCompatActivity implements OnPostActi
account_follow.setText(R.string.action_follow);
doAction = action.FOLLOW;
}else{
account_follow.setText(R.string.action_no_action);
account_follow.getBackground().setColorFilter(ContextCompat.getColor(this, R.color.red_1), PorterDuff.Mode.MULTIPLY);
doAction = action.NOTHING;
}
if( doAction == action.NOTHING){
account_follow.setEnabled(false);
}else {
account_follow.setEnabled(true);
}
//The authenticated account is followed by the account
if( relationship.isFollowed_by()){

View File

@ -37,6 +37,8 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
@ -96,6 +98,7 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc
private DisplayImageOptions options;
private LinearLayout toot_picture_container;
private List<Attachment> attachments;
private boolean isSensitive = false;
private ImageButton toot_visibility;
private Button toot_it;
private EditText toot_content, toot_cw_content;
@ -104,6 +107,8 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc
private ListView toot_lv_accounts;
private BroadcastReceiver search_validate;
private Status tootReply = null;
private String sharedContent;
private CheckBox toot_sensitive;
private String pattern = "^.*(@([a-zA-Z0-9_]{2,}))$";
@Override
@ -127,11 +132,12 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc
toot_reply_content_container = (LinearLayout) findViewById(R.id.toot_reply_content_container);
toot_show_accounts = (RelativeLayout) findViewById(R.id.toot_show_accounts);
toot_lv_accounts = (ListView) findViewById(R.id.toot_lv_accounts);
toot_sensitive = (CheckBox) findViewById(R.id.toot_sensitive);
Bundle b = getIntent().getExtras();
if(b != null) {
tootReply = b.getParcelable("tootReply");
sharedContent = b.getString("sharedContent", null);
}
if( tootReply != null) {
setTitle(R.string.toot_title_reply);
@ -185,6 +191,9 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc
}else {
setTitle(R.string.toot_title);
}
if( sharedContent != null ){ //Shared content
toot_content.setText( String.format("\n%s", sharedContent));
}
attachments = new ArrayList<>();
charsInCw = 0;
charsInToot = 0;
@ -244,6 +253,12 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc
toot_visibility.setImageResource(R.drawable.ic_action_globe);
}
toot_sensitive.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
isSensitive = isChecked;
}
});
toot_space_left.setText(String.valueOf((maxChar - (charsInToot + charsInCw))));
toot_cw.setOnClickListener(new View.OnClickListener() {
@ -280,6 +295,7 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc
return;
}
Status toot = new Status();
toot.setSensitive(isSensitive);
toot.setMedia_attachments(attachments);
if( toot_cw_content.getText().toString().trim().length() > 0)
toot.setSpoiler_text(toot_cw_content.getText().toString().trim());
@ -429,6 +445,7 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc
attachments.add(attachment);
if( attachments.size() < 4)
toot_picture.setEnabled(true);
toot_sensitive.setVisibility(View.VISIBLE);
}
}
@ -458,6 +475,11 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc
View namebar = findViewById(viewId);
((ViewGroup) namebar.getParent()).removeView(namebar);
dialog.dismiss();
if( attachments.size() == 0 ) {
toot_sensitive.setVisibility(View.GONE);
isSensitive = false;
toot_sensitive.setChecked(false);
}
}
});
dialog.show();
@ -529,6 +551,8 @@ public class TootActivity extends AppCompatActivity implements OnRetrieveSearcAc
attachments.removeAll(tmp_attachment);
tmp_attachment.clear();
}
isSensitive = false;
toot_sensitive.setVisibility(View.GONE);
Toast.makeText(TootActivity.this,R.string.toot_sent, Toast.LENGTH_LONG).show();
}else {
Toast.makeText(TootActivity.this,R.string.toast_error, Toast.LENGTH_LONG).show();

View File

@ -16,11 +16,8 @@ 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.Account;
import fr.gouv.etalab.mastodon.helper.Helper;
import fr.gouv.etalab.mastodon.interfaces.OnPostActionInterface;
@ -34,25 +31,24 @@ public class PostActionAsyncTask extends AsyncTask<Void, Void, Void> {
private Context context;
private OnPostActionInterface listener;
private int statusCode;
private API.StatusAction statusAction;
private String statusId;
private API.StatusAction apiAction;
private String targetedId;
private String comment;
private Account account;
private fr.gouv.etalab.mastodon.client.Entities.Status status;
private API api;
public PostActionAsyncTask(Context context, API.StatusAction statusAction, String statusId, OnPostActionInterface onPostActionInterface){
public PostActionAsyncTask(Context context, API.StatusAction apiAction, String targetedId, OnPostActionInterface onPostActionInterface){
this.context = context;
this.listener = onPostActionInterface;
this.statusAction = statusAction;
this.statusId = statusId;
this.apiAction = apiAction;
this.targetedId = targetedId;
}
public PostActionAsyncTask(Context context, API.StatusAction statusAction, String statusId, fr.gouv.etalab.mastodon.client.Entities.Status status, String comment, OnPostActionInterface onPostActionInterface){
public PostActionAsyncTask(Context context, API.StatusAction apiAction, String targetedId, fr.gouv.etalab.mastodon.client.Entities.Status status, String comment, OnPostActionInterface onPostActionInterface){
this.context = context;
this.listener = onPostActionInterface;
this.statusAction = statusAction;
this.statusId = statusId;
this.apiAction = apiAction;
this.targetedId = targetedId;
this.comment = comment;
this.status = status;
}
@ -61,18 +57,18 @@ public class PostActionAsyncTask extends AsyncTask<Void, Void, Void> {
protected Void doInBackground(Void... params) {
api = new API(context);
if(statusAction == API.StatusAction.REPORT)
if(apiAction == API.StatusAction.REPORT)
statusCode = api.reportAction(status, comment);
else if(statusAction == API.StatusAction.CREATESTATUS)
else if(apiAction == API.StatusAction.CREATESTATUS)
statusCode = api.statusAction(status);
else
statusCode = api.postAction(statusAction, statusId);
statusCode = api.postAction(apiAction, targetedId);
return null;
}
@Override
protected void onPostExecute(Void result) {
listener.onPostAction(statusCode, statusAction, statusId, api.getError());
listener.onPostAction(statusCode, apiAction, targetedId, api.getError());
}
}

View File

@ -0,0 +1,56 @@
/* Copyright 2017 Thomas Schneider
*
* This file is a part of Mastodon Etalab for mastodon.etalab.gouv.fr
*
* 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.
*
* Mastodon Etalab 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 Thomas Schneider; if not,
* see <http://www.gnu.org/licenses>. */
package fr.gouv.etalab.mastodon.asynctasks;
import android.content.Context;
import android.os.AsyncTask;
import fr.gouv.etalab.mastodon.client.API;
import fr.gouv.etalab.mastodon.client.APIResponse;
import fr.gouv.etalab.mastodon.interfaces.OnRetrieveAccountsInterface;
/**
* Created by Thomas on 07/06/2017.
* Retrieves follow requests sent for the authenticated account
*/
public class RetrieveFollowRequestSentAsyncTask extends AsyncTask<Void, Void, Void> {
private Context context;
private APIResponse apiResponse;
private String max_id;
private OnRetrieveAccountsInterface listener;
public RetrieveFollowRequestSentAsyncTask(Context context, String max_id, OnRetrieveAccountsInterface onRetrieveAccountsInterface){
this.context = context;
this.max_id = max_id;
this.listener = onRetrieveAccountsInterface;
}
@Override
protected Void doInBackground(Void... params) {
apiResponse = new API(context).getFollowRequest(max_id);
return null;
}
@Override
protected void onPostExecute(Void result) {
listener.onRetrieveAccounts(apiResponse);
}
}

View File

@ -90,6 +90,8 @@ public class API {
UNFOLLOW,
CREATESTATUS,
UNSTATUS,
AUTHORIZE,
REJECT,
REPORT
}
@ -294,7 +296,7 @@ public class API {
* @param max_id String id max
* @param since_id String since the id
* @param limit int limit - max value 40
* @return List<Status>
* @return APIResponse
*/
private APIResponse getStatus(String accountId, boolean onlyMedia,
boolean exclude_replies, String max_id, String since_id, int limit) {
@ -413,7 +415,7 @@ public class API {
* @param max_id String id max
* @param since_id String since the id
* @param limit int limit - max value 40
* @return List<Status>
* @return APIResponse
*/
private APIResponse getHomeTimeline(String max_id, String since_id, int limit) {
@ -466,7 +468,7 @@ public class API {
* @param max_id String id max
* @param since_id String since the id
* @param limit int limit - max value 40
* @return List<Status>
* @return APIResponse
*/
private APIResponse getPublicTimeline(boolean local, String max_id, String since_id, int limit){
@ -522,7 +524,7 @@ public class API {
* @param max_id String id max
* @param since_id String since the id
* @param limit int limit - max value 40
* @return List<Status>
* @return APIResponse
*/
private APIResponse getPublicTimelineTag(String tag, boolean local, String max_id, String since_id, int limit){
@ -607,7 +609,7 @@ public class API {
* @param max_id String id max
* @param since_id String since the id
* @param limit int limit - max value 40
* @return List<Status>
* @return APIResponse
*/
private APIResponse getAccounts(String action, String max_id, String since_id, int limit){
@ -645,6 +647,57 @@ public class API {
}
/**
* Retrieves follow requests for the authenticated account *synchronously*
* @param max_id String id max
* @return APIResponse
*/
public APIResponse getFollowRequest(String max_id){
return getFollowRequest(max_id, null, accountPerPage);
}
/**
* Retrieves follow requests for the authenticated account *synchronously*
* @param max_id String id max
* @param since_id String since the id
* @param limit int limit - max value 40
* @return APIResponse
*/
private APIResponse getFollowRequest(String max_id, String since_id, int limit){
RequestParams params = new RequestParams();
if( max_id != null )
params.put("max_id", max_id);
if( since_id != null )
params.put("since_id", since_id);
if( 0 > limit || limit > 40)
limit = 40;
params.put("limit",String.valueOf(limit));
accounts = new ArrayList<>();
get("/follow_requests", params, new JsonHttpResponseHandler() {
@Override
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
apiResponse.setSince_id(findSinceId(headers));
apiResponse.setMax_id(findMaxId(headers));
Account account = parseAccountResponse(response);
accounts.add(account);
}
@Override
public void onSuccess(int statusCode, Header[] headers, JSONArray response) {
apiResponse.setSince_id(findSinceId(headers));
apiResponse.setMax_id(findMaxId(headers));
accounts = parseAccountResponse(response);
}
@Override
public void onFailure(int statusCode, Header[] headers, Throwable error, JSONObject response){
setError(statusCode, error);
}
});
apiResponse.setAccounts(accounts);
return apiResponse;
}
/**
* Retrieves favourited status for the authenticated account *synchronously*
* @param max_id String id max
@ -658,7 +711,7 @@ public class API {
* @param max_id String id max
* @param since_id String since the id
* @param limit int limit - max value 40
* @return List<Status>
* @return APIResponse
*/
private APIResponse getFavourites(String max_id, String since_id, int limit){
@ -706,6 +759,7 @@ public class API {
return postAction(statusAction, targetedId, null, null);
}
/**
* Makes the post action
* @param status Status object related to the status
@ -766,6 +820,12 @@ public class API {
case UNSTATUS:
action = String.format("/statuses/%s", targetedId);
break;
case AUTHORIZE:
action = String.format("/follow_requests/%s/authorize", targetedId);
break;
case REJECT:
action = String.format("/follow_requests/%s/reject", targetedId);
break;
case REPORT:
action = "/reports";
params = new RequestParams();
@ -774,7 +834,6 @@ public class API {
params.put("status_ids[]", status.getId());
break;
case CREATESTATUS:
params = new RequestParams();
action = "/statuses";
params.put("status", status.getContent());
@ -860,7 +919,7 @@ public class API {
* @param max_id String id max
* @param since_id String since the id
* @param limit int limit - max value 40
* @return List<Notification>
* @return APIResponse
*/
private APIResponse getNotifications(String max_id, String since_id, int limit){
@ -897,6 +956,11 @@ public class API {
return apiResponse;
}
/**
* Upload media
* @param inputStream InputStream
* @return Attachment
*/
public Attachment uploadMedia(InputStream inputStream){
RequestParams params = new RequestParams();
@ -1087,7 +1151,7 @@ public class API {
status.setCreated_at(Helper.mstStringToDate(context, resobj.get("created_at").toString()));
status.setIn_reply_to_id(resobj.get("in_reply_to_id").toString());
status.setIn_reply_to_account_id(resobj.get("in_reply_to_account_id").toString());
status.setSensitive(Boolean.getBoolean(resobj.get("sensitive").toString()));
status.setSensitive(Boolean.parseBoolean(resobj.get("sensitive").toString()));
status.setSpoiler_text(resobj.get("spoiler_text").toString());
status.setVisibility(resobj.get("visibility").toString());

View File

@ -45,6 +45,7 @@ public class Status implements Parcelable {
private String spoiler_text;
private String visibility;
private boolean attachmentShown = false;
private boolean spoilerShown = false;
private List<Attachment> media_attachments;
private List<Mention> mentions;
private List<Tag> tags;
@ -69,6 +70,7 @@ public class Status implements Parcelable {
spoiler_text = in.readString();
visibility = in.readString();
attachmentShown = in.readByte() != 0;
spoilerShown = in.readByte() != 0;
}
public Status(){}
@ -279,5 +281,14 @@ public class Status implements Parcelable {
dest.writeString(spoiler_text);
dest.writeString(visibility);
dest.writeByte((byte) (attachmentShown ? 1 : 0));
dest.writeByte((byte) (spoilerShown ? 1 : 0));
}
public boolean isSpoilerShown() {
return spoilerShown;
}
public void setSpoilerShown(boolean spoilerShown) {
this.spoilerShown = spoilerShown;
}
}

View File

@ -0,0 +1,182 @@
package fr.gouv.etalab.mastodon.drawers;
/* Copyright 2017 Thomas Schneider
*
* This file is a part of Mastodon Etalab for mastodon.etalab.gouv.fr
*
* 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.
*
* Mastodon Etalab 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 Thomas Schneider; if not,
* see <http://www.gnu.org/licenses>. */
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.PorterDuff;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.content.ContextCompat;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer;
import java.util.ArrayList;
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.helper.Helper;
import fr.gouv.etalab.mastodon.interfaces.OnPostActionInterface;
import mastodon.etalab.gouv.fr.mastodon.R;
/**
* Created by Thomas on 07/05/2017.
* Adapter for accounts asking a follow request
*/
public class AccountsFollowRequestAdapter extends BaseAdapter implements OnPostActionInterface {
private List<Account> accounts;
private LayoutInflater layoutInflater;
private ImageLoader imageLoader;
private DisplayImageOptions options;
private Context context;
private AccountsFollowRequestAdapter accountsFollowRequestAdapter;
public AccountsFollowRequestAdapter(Context context, List<Account> accounts){
this.accounts = accounts;
layoutInflater = LayoutInflater.from(context);
imageLoader = ImageLoader.getInstance();
this.context = context;
options = new DisplayImageOptions.Builder().displayer(new SimpleBitmapDisplayer()).cacheInMemory(false)
.cacheOnDisk(true).resetViewBeforeLoading(true).build();
accountsFollowRequestAdapter = this;
}
@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) {
final Account account = accounts.get(position);
final ViewHolder holder;
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.drawer_account_follow_request, parent, false);
holder = new ViewHolder();
holder.account_pp = (ImageView) convertView.findViewById(R.id.account_pp);
holder.account_un = (TextView) convertView.findViewById(R.id.account_un);
holder.btn_authorize = (Button) convertView.findViewById(R.id.btn_authorize);
holder.btn_reject = (Button) convertView.findViewById(R.id.btn_reject);
holder.account_container = (LinearLayout) convertView.findViewById(R.id.account_container);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.btn_authorize.getBackground().setColorFilter(ContextCompat.getColor(context, R.color.green_1), PorterDuff.Mode.MULTIPLY);
holder.btn_reject.getBackground().setColorFilter(ContextCompat.getColor(context, R.color.red_1), PorterDuff.Mode.MULTIPLY);
holder.account_un.setText(String.format("@%s", account.getUsername()));
//Profile picture
imageLoader.displayImage(account.getAvatar(), holder.account_pp, options);
holder.account_pp.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
openAccountDetails(account.getId());
}
});
holder.account_un.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
openAccountDetails(account.getId());
}
});
holder.btn_authorize.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new PostActionAsyncTask(context, API.StatusAction.AUTHORIZE, account.getId(), AccountsFollowRequestAdapter.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
});
holder.btn_reject.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new PostActionAsyncTask(context, API.StatusAction.REJECT, account.getId(), AccountsFollowRequestAdapter.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
});
return convertView;
}
private void openAccountDetails(String userId){
Intent intent = new Intent(context, ShowAccountActivity.class);
Bundle b = new Bundle();
b.putString("accountId", userId);
intent.putExtras(b);
context.startActivity(intent);
}
@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();
return;
}
Helper.manageMessageStatusCode(context, statusCode, statusAction);
//When authorizing or rejecting an account, this account is removed from the list
List<Account> accountToRemove = new ArrayList<>();
if( statusAction == API.StatusAction.AUTHORIZE || statusAction == API.StatusAction.REJECT){
for(Account account: accounts){
if( account.getId().equals(userId))
accountToRemove.add(account);
}
accounts.removeAll(accountToRemove);
accountsFollowRequestAdapter.notifyDataSetChanged();
}
}
private class ViewHolder {
ImageView account_pp;
Button btn_authorize;
Button btn_reject;
TextView account_un;
LinearLayout account_container;
}
}

View File

@ -37,6 +37,7 @@ import android.widget.Toast;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer;
import com.vdurmont.emoji.EmojiParser;
import java.util.ArrayList;
import java.util.List;
@ -151,7 +152,7 @@ public class AccountsListAdapter extends BaseAdapter implements OnPostActionInte
holder.account_ds.setVisibility(View.VISIBLE);
}
});
holder.account_dn.setText(account.getDisplay_name());
holder.account_dn.setText(EmojiParser.parseToUnicode(account.getDisplay_name()));
holder.account_un.setText(String.format("@%s",account.getUsername()));
holder.account_ac.setText(account.getAcct());
if( account.getDisplay_name().equals(account.getAcct()))

View File

@ -29,6 +29,8 @@ import android.widget.TextView;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer;
import com.vdurmont.emoji.EmojiParser;
import java.util.List;
import fr.gouv.etalab.mastodon.client.Entities.Account;
import fr.gouv.etalab.mastodon.helper.Helper;
@ -90,7 +92,7 @@ public class AccountsSearchAdapter extends BaseAdapter {
holder = (ViewHolder) convertView.getTag();
}
holder.account_dn.setText(account.getDisplay_name());
holder.account_dn.setText(EmojiParser.parseToUnicode(account.getDisplay_name()));
holder.account_un.setText(String.format("@%s",account.getUsername()));
//Profile picture
imageLoader.displayImage(account.getAvatar(), holder.account_pp, options);

View File

@ -42,6 +42,7 @@ import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer;
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
import com.vdurmont.emoji.EmojiParser;
import java.util.List;
@ -220,7 +221,7 @@ public class NotificationsListAdapter extends BaseAdapter {
});
holder.notification_account_displayname.setText(notification.getAccount().getDisplay_name());
holder.notification_account_displayname.setText(EmojiParser.parseToUnicode(notification.getAccount().getDisplay_name()));
holder.notification_account_username.setText( String.format("@%s",notification.getAccount().getUsername()));
//Profile picture
imageLoader.displayImage(notification.getAccount().getAvatar(), holder.notification_account_profile, options);

View File

@ -34,6 +34,7 @@ import android.widget.TextView;
import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer;
import com.vdurmont.emoji.EmojiParser;
import java.util.ArrayList;
import java.util.List;
@ -167,7 +168,7 @@ public class SearchListAdapter extends BaseAdapter {
final String content, displayName, username, ppurl;
if( status.getReblog() != null){
content = status.getReblog().getContent();
displayName = status.getReblog().getAccount().getDisplay_name();
displayName = EmojiParser.parseToUnicode(status.getReblog().getAccount().getDisplay_name());
username = status.getReblog().getAccount().getUsername();
holder.status_reblog_user.setText(displayName + " " +String.format("@%s",username));
ppurl = status.getReblog().getAccount().getAvatar();
@ -177,7 +178,7 @@ public class SearchListAdapter extends BaseAdapter {
}else {
ppurl = status.getAccount().getAvatar();
content = status.getContent();
displayName = status.getAccount().getDisplay_name();
displayName = EmojiParser.parseToUnicode(status.getAccount().getDisplay_name());
username = status.getAccount().getUsername();
holder.status_reblog_user.setVisibility(View.GONE);
holder.status_account_displayname.setText(displayName);

View File

@ -55,6 +55,7 @@ import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.assist.FailReason;
import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer;
import com.nostra13.universalimageloader.core.listener.SimpleImageLoadingListener;
import com.vdurmont.emoji.EmojiParser;
import java.util.ArrayList;
import java.util.List;
@ -154,10 +155,34 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf
holder.status_reply = (ImageView) convertView.findViewById(R.id.status_reply);
holder.status_privacy = (ImageView) convertView.findViewById(R.id.status_privacy);
holder.main_container = (LinearLayout) convertView.findViewById(R.id.main_container);
holder.status_spoiler_container = (LinearLayout) convertView.findViewById(R.id.status_spoiler_container);
holder.status_content_container = (LinearLayout) convertView.findViewById(R.id.status_content_container);
holder.status_spoiler = (TextView) convertView.findViewById(R.id.status_spoiler);
holder.status_spoiler_button = (Button) convertView.findViewById(R.id.status_spoiler_button);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
if( status.getSpoiler_text() != null && status.getSpoiler_text().trim().length() > 0 && !status.isSpoilerShown()){
holder.status_spoiler_container.setVisibility(View.VISIBLE);
holder.status_content_container.setVisibility(View.GONE);
}else {
holder.status_spoiler_button.setVisibility(View.GONE);
holder.status_content_container.setVisibility(View.VISIBLE);
}
if( status.getSpoiler_text() != null)
holder.status_spoiler.setText(status.getSpoiler_text());
//Spoiler opens
holder.status_spoiler_button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
status.setSpoilerShown(true);
holder.status_spoiler_button.setVisibility(View.GONE);
statusListAdapter.notifyDataSetChanged();
}
});
//Hides action bottom bar action when looking to status trough accounts
if( type == RetrieveFeedsAsyncTask.Type.USER){
@ -208,7 +233,7 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf
}else {
ppurl = status.getAccount().getAvatar();
content = status.getContent();
displayName = status.getAccount().getDisplay_name();
displayName = EmojiParser.parseToUnicode(status.getAccount().getDisplay_name());
username = status.getAccount().getUsername();
holder.status_reblog_user.setVisibility(View.GONE);
holder.status_account_displayname.setText(displayName);
@ -239,16 +264,19 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf
holder.status_toot_date.setText(Helper.dateDiff(context, status.getCreated_at()));
imageLoader.displayImage(ppurl, holder.status_account_profile, options);
if( status.getMedia_attachments().size() < 1) {
holder.status_document_container.setVisibility(View.GONE);
holder.status_show_more.setVisibility(View.GONE);
}else{
if(behaviorWithAttachments == Helper.ATTACHMENT_ALWAYS || ( behaviorWithAttachments == Helper.ATTACHMENT_WIFI && isOnWifi)){
//If medias are loaded without any conditions or if device is on wifi
if(! status.isSensitive() && (behaviorWithAttachments == Helper.ATTACHMENT_ALWAYS || ( behaviorWithAttachments == Helper.ATTACHMENT_WIFI && isOnWifi)) ){
loadAttachments(status);
holder.status_show_more.setVisibility(View.GONE);
status.setAttachmentShown(true);
}else{
//Text depending if toots is sensitive or not
String textShowMore = (status.isSensitive())?context.getString(R.string.load_sensitive_attachment):context.getString(R.string.load_attachment);
holder.status_show_more.setText(textShowMore);
if( !status.isAttachmentShown() ) {
holder.status_show_more.setVisibility(View.VISIBLE);
holder.status_document_container.setVisibility(View.GONE);
@ -559,6 +587,11 @@ public class StatusListAdapter extends BaseAdapter implements OnPostActionInterf
private class ViewHolder {
LinearLayout status_content_container;
LinearLayout status_spoiler_container;
TextView status_spoiler;
Button status_spoiler_button;
TextView status_content;
TextView status_account_username;
TextView status_account_displayname;

View File

@ -0,0 +1,178 @@
package fr.gouv.etalab.mastodon.fragments;
/* Copyright 2017 Thomas Schneider
*
* This file is a part of Mastodon Etalab for mastodon.etalab.gouv.fr
*
* 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.
*
* Mastodon Etalab 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 Thomas Schneider; if not,
* see <http://www.gnu.org/licenses>. */
import android.content.Context;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.widget.SwipeRefreshLayout;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
import fr.gouv.etalab.mastodon.asynctasks.RetrieveFollowRequestSentAsyncTask;
import fr.gouv.etalab.mastodon.client.APIResponse;
import fr.gouv.etalab.mastodon.client.Entities.Account;
import fr.gouv.etalab.mastodon.drawers.AccountsFollowRequestAdapter;
import fr.gouv.etalab.mastodon.helper.Helper;
import fr.gouv.etalab.mastodon.interfaces.OnRetrieveAccountsInterface;
import mastodon.etalab.gouv.fr.mastodon.R;
/**
* Created by Thomas on 07/06/2017.
* Fragment to display follow requests for the authenticated account
*/
public class DisplayFollowRequestSentFragment extends Fragment implements OnRetrieveAccountsInterface {
private boolean flag_loading;
private Context context;
private AsyncTask<Void, Void, Void> asyncTask;
private AccountsFollowRequestAdapter accountsFollowRequestAdapter;
private String max_id;
private List<Account> accounts;
private RelativeLayout mainLoader, nextElementLoader, textviewNoAction;
private boolean firstLoad;
private SwipeRefreshLayout swipeRefreshLayout;
private int accountPerPage;
private TextView no_action_text;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//View for fragment is the same that fragment accounts
View rootView = inflater.inflate(R.layout.fragment_accounts, container, false);
context = getContext();
accounts = new ArrayList<>();
max_id = null;
firstLoad = true;
flag_loading = true;
swipeRefreshLayout = (SwipeRefreshLayout) rootView.findViewById(R.id.swipeContainer);
SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
accountPerPage = sharedpreferences.getInt(Helper.SET_ACCOUNTS_PER_PAGE, 40);
final ListView lv_accounts = (ListView) rootView.findViewById(R.id.lv_accounts);
no_action_text = (TextView) rootView.findViewById(R.id.no_action_text);
mainLoader = (RelativeLayout) rootView.findViewById(R.id.loader);
nextElementLoader = (RelativeLayout) rootView.findViewById(R.id.loading_next_accounts);
textviewNoAction = (RelativeLayout) rootView.findViewById(R.id.no_action);
mainLoader.setVisibility(View.VISIBLE);
nextElementLoader.setVisibility(View.GONE);
accountsFollowRequestAdapter = new AccountsFollowRequestAdapter(context, this.accounts);
lv_accounts.setAdapter(accountsFollowRequestAdapter);
lv_accounts.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (firstVisibleItem + visibleItemCount == totalItemCount) {
if (!flag_loading) {
flag_loading = true;
asyncTask = new RetrieveFollowRequestSentAsyncTask(context, max_id, DisplayFollowRequestSentFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
nextElementLoader.setVisibility(View.VISIBLE);
}
} else {
nextElementLoader.setVisibility(View.GONE);
}
}
});
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
max_id = null;
accounts = new ArrayList<>();
firstLoad = true;
flag_loading = true;
asyncTask = new RetrieveFollowRequestSentAsyncTask(context, max_id, DisplayFollowRequestSentFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
});
swipeRefreshLayout.setColorSchemeResources(R.color.colorAccent,
R.color.colorPrimary,
R.color.colorPrimaryDark);
asyncTask = new RetrieveFollowRequestSentAsyncTask(context, max_id, DisplayFollowRequestSentFragment.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
return rootView;
}
@Override
public void onCreate(Bundle saveInstance)
{
super.onCreate(saveInstance);
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
this.context = context;
}
public void onStop() {
super.onStop();
if(asyncTask != null && asyncTask.getStatus() == AsyncTask.Status.RUNNING)
asyncTask.cancel(true);
}
@Override
public void onRetrieveAccounts(APIResponse apiResponse) {
mainLoader.setVisibility(View.GONE);
nextElementLoader.setVisibility(View.GONE);
if( apiResponse.getError() != 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(getContext(), apiResponse.getError().getError(),Toast.LENGTH_LONG).show();
return;
}
List<Account> accounts = apiResponse.getAccounts();
if( firstLoad && (accounts == null || accounts.size() == 0)) {
no_action_text.setText(context.getString(R.string.no_follow_request));
textviewNoAction.setVisibility(View.VISIBLE);
}else
textviewNoAction.setVisibility(View.GONE);
max_id = apiResponse.getMax_id();
if( accounts != null) {
for(Account tmpAccount: accounts){
this.accounts.add(tmpAccount);
}
accountsFollowRequestAdapter.notifyDataSetChanged();
}
swipeRefreshLayout.setRefreshing(false);
firstLoad = false;
flag_loading = accounts != null && accounts.size() < accountPerPage;
}
}

View File

@ -248,7 +248,8 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn
mainLoader.setVisibility(View.GONE);
nextElementLoader.setVisibility(View.GONE);
if( apiResponse.getError() != null){
//Discards 404 - error which can often happen due to toots which have been deleted
if( apiResponse.getError() != null && !apiResponse.getError().getError().startsWith("404 -")){
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)
@ -272,7 +273,7 @@ public class DisplayStatusFragment extends Fragment implements OnRetrieveFeedsIn
flag_loading = statuses != null && statuses.size() < tootsPerPage;
//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 = getContext().getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
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();

View File

@ -532,10 +532,6 @@ public class Helper {
Toast.makeText(activity, activity.getString(R.string.toast_account_changed, "@" + account.getAcct() + "@" + account.getInstance()), Toast.LENGTH_LONG).show();
changeUser(activity, userId);
arrow.setImageResource(R.drawable.ic_arrow_drop_down);
navigationView.getMenu().clear();
navigationView.inflateMenu(R.menu.activity_main_drawer);
navigationView.setCheckedItem(R.id.nav_home);
navigationView.getMenu().performIdentifierAction(R.id.nav_home, 0);
return true;
}
return false;
@ -595,9 +591,19 @@ public class Helper {
*/
public static void changeUser(Activity activity, String userID) {
final NavigationView navigationView = (NavigationView) activity.findViewById(R.id.nav_view);
navigationView.getMenu().clear();
navigationView.inflateMenu(R.menu.activity_main_drawer);
navigationView.setCheckedItem(R.id.nav_home);
navigationView.getMenu().performIdentifierAction(R.id.nav_home, 0);
SQLiteDatabase db = Sqlite.getInstance(activity, Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open();
Account account = new AccountDAO(activity,db).getAccountByID(userID);
//Locked account can see follow request
if (account.isLocked()) {
navigationView.getMenu().findItem(R.id.nav_follow_request).setVisible(true);
} else {
navigationView.getMenu().findItem(R.id.nav_follow_request).setVisible(false);
}
SharedPreferences sharedpreferences = activity.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putString(Helper.PREF_KEY_OAUTH_TOKEN, account.getToken());
@ -607,7 +613,6 @@ public class Helper {
DisplayImageOptions options = new DisplayImageOptions.Builder().displayer(new SimpleBitmapDisplayer()).cacheInMemory(false)
.cacheOnDisk(true).resetViewBeforeLoading(true).build();
imageLoader = ImageLoader.getInstance();
NavigationView navigationView = (NavigationView) activity.findViewById(R.id.nav_view);
View headerLayout = navigationView.getHeaderView(0);
updateHeaderAccountInfo(activity, account, headerLayout, imageLoader, options);
}

View File

@ -22,7 +22,6 @@ import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import android.util.Log;
import com.evernote.android.job.Job;
import com.evernote.android.job.JobManager;
@ -62,8 +61,6 @@ import static fr.gouv.etalab.mastodon.helper.Helper.notify_user;
public class HomeTimelineSyncJob extends Job implements OnRetrieveHomeTimelineServiceInterface{
static final String HOME_TIMELINE = "home_timeline";
private int notificationId;
@NonNull
@Override
@ -115,8 +112,6 @@ public class HomeTimelineSyncJob extends Job implements OnRetrieveHomeTimelineSe
//Retrieve users in db that owner has.
for (Account account: accounts) {
String since_id = sharedpreferences.getString(Helper.LAST_HOMETIMELINE_MAX_ID + account.getId(), null);
long notif_id = Long.parseLong(account.getId());
notificationId = ((notif_id + 2) > 2147483647 )?(int)(2147483647 - notif_id -2):(int)(notif_id + 2);
new RetrieveHomeTimelineServiceAsyncTask(getContext(), account.getInstance(), account.getToken(), since_id, account.getAcct(), account.getId(), HomeTimelineSyncJob.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
@ -171,6 +166,8 @@ public class HomeTimelineSyncJob extends Job implements OnRetrieveHomeTimelineSe
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);
int notificationId = ((notif_id + 2) > 2147483647) ? (int) (2147483647 - notif_id - 2) : (int) (notif_id + 2);
if( max_id != null)
notify_user(getContext(), intent, notificationId, icon_notification,title,message);
SharedPreferences.Editor editor = sharedpreferences.edit();

View File

@ -22,21 +22,16 @@ import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import android.util.Log;
import com.evernote.android.job.Job;
import com.evernote.android.job.JobManager;
import com.evernote.android.job.JobRequest;
import com.nostra13.universalimageloader.cache.disc.impl.UnlimitedDiskCache;
import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.ImageLoaderConfiguration;
import java.io.File;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import fr.gouv.etalab.mastodon.activities.MainActivity;
import fr.gouv.etalab.mastodon.client.APIResponse;
import fr.gouv.etalab.mastodon.client.PatchBaseImageDownloader;
@ -63,7 +58,6 @@ import static fr.gouv.etalab.mastodon.helper.Helper.notify_user;
public class NotificationsSyncJob extends Job implements OnRetrieveNotificationsInterface{
static final String NOTIFICATION_REFRESH = "job_notification";
private int notificationId;
@NonNull
@Override
@ -121,8 +115,6 @@ public class NotificationsSyncJob extends Job implements OnRetrieveNotifications
//Retrieve users in db that owner has.
for (Account account: accounts) {
String max_id = sharedpreferences.getString(Helper.LAST_NOTIFICATION_MAX_ID + account.getId(), null);
long notif_id = Long.parseLong(account.getId());
notificationId = ((notif_id + 1) > 2147483647 )?(int)(2147483647 - notif_id - 1):(int)(notif_id + 1);
new RetrieveNotificationsAsyncTask(getContext(), account.getInstance(), account.getToken(), max_id, account.getAcct(), account.getId(), NotificationsSyncJob.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
@ -226,6 +218,8 @@ public class NotificationsSyncJob extends Job implements OnRetrieveNotifications
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);
int notificationId = ((notif_id + 1) > 2147483647) ? (int) (2147483647 - notif_id - 1) : (int) (notif_id + 1);
if( max_id != null)
notify_user(getContext(), intent, notificationId, icon_notification,title,message);
}

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="-100%" android:toXDelta="0%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="300"/>
</set>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="100%" android:toXDelta="0%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="300" />
</set>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="0%" android:toXDelta="-100%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="300"/>
</set>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:fromXDelta="0%" android:toXDelta="100%"
android:fromYDelta="0%" android:toYDelta="0%"
android:duration="300" />
</set>

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 508 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 652 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 441 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 940 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 593 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -44,6 +44,7 @@
</android.support.design.widget.AppBarLayout>
<!-- Framelayout to display Fragments -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_app_container"

View File

@ -51,12 +51,14 @@
android:id="@+id/account_dn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="1"
android:textSize="18sp"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
<TextView
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:id="@+id/account_un"
android:maxLines="1"
android:layout_width="wrap_content"
android:textSize="14sp"
android:layout_height="wrap_content"
@ -69,12 +71,14 @@
android:gravity="center"
android:textColor="@color/colorPrimary"
android:text="@string/followed_by"
android:maxLines="1"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:layout_marginTop="5dp"
android:id="@+id/account_ac"
android:gravity="center"
android:maxLines="1"
android:textSize="16sp"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
@ -96,9 +100,8 @@
</LinearLayout>
</LinearLayout>
<TextView
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:id="@+id/account_note"
android:padding="5dp"
android:gravity="center"
android:layout_width="match_parent"
android:maxLines="3"
@ -107,6 +110,7 @@
<android.support.design.widget.AppBarLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_marginTop="5dp"
android:layout_width="match_parent"
android:background="@android:color/white"
android:layout_height="wrap_content">

View File

@ -165,5 +165,11 @@
android:layout_height="wrap_content" />
</LinearLayout>
<CheckBox
android:text="@string/toot_sensitive"
android:visibility="gone"
android:id="@+id/toot_sensitive"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>

View File

@ -72,12 +72,14 @@
android:orientation="horizontal">
<TextView
android:id="@+id/account_dn"
android:maxLines="1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="18sp"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
<TextView
android:id="@+id/account_un"
android:maxLines="1"
android:layout_width="wrap_content"
android:textSize="14sp"
android:layout_height="wrap_content"
@ -88,6 +90,7 @@
android:visibility="gone"
android:id="@+id/account_ac"
android:textSize="16sp"
android:maxLines="1"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<LinearLayout

View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2017 Thomas Schneider
This file is a part of Mastodon Etalab for mastodon.etalab.gouv.fr
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.
Mastodon Etalab 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 Thomas Schneider; if not,
see <http://www.gnu.org/licenses>.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp"
android:id="@+id/account_container"
android:orientation="horizontal">
<ImageView
android:layout_gravity="center"
android:id="@+id/account_pp"
android:layout_width="60dp"
android:layout_height="60dp"
tools:ignore="ContentDescription" />
<TextView
android:id="@+id/account_un"
android:padding="5dp"
android:maxLines="1"
android:gravity="center"
android:textSize="16sp"
android:layout_gravity="center"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content" />
<LinearLayout
android:layout_width="wrap_content"
android:gravity="end"
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
android:id="@+id/btn_authorize"
android:text="@string/authorize"
android:layout_marginStart="5dp"
android:layout_marginLeft="5dp"
android:layout_gravity="end"
android:gravity="center"
android:textColor="@color/white"
style="@style/Base.Widget.AppCompat.Button.Small"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/btn_reject"
android:layout_marginStart="5dp"
android:layout_marginLeft="5dp"
android:text="@string/reject"
android:layout_gravity="end"
android:gravity="center"
android:textColor="@color/white"
style="@style/Base.Widget.AppCompat.Button.Small"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>

View File

@ -83,83 +83,120 @@
android:layout_gravity="end"
android:gravity="end"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/status_content"
android:layout_marginTop="10dp"
android:autoLink="web"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<LinearLayout
android:id="@+id/status_document_container"
android:orientation="horizontal"
android:id="@+id/status_spoiler_container"
android:layout_width="match_parent"
android:layout_height="200dp">
<ImageView
android:id="@+id/status_prev1"
android:layout_width="0dp"
android:layout_weight="1"
android:scaleType="centerCrop"
android:layout_height="match_parent"
tools:ignore="ContentDescription" />
android:orientation="vertical"
android:visibility="gone"
android:layout_height="wrap_content">
<TextView
android:id="@+id/status_spoiler"
android:layout_marginTop="10dp"
android:autoLink="web"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/status_spoiler_button"
android:textAllCaps="false"
android:drawableLeft="@drawable/ic_remove_red_eye"
android:drawableStart="@drawable/ic_remove_red_eye"
android:gravity="center"
android:drawablePadding="5dp"
android:paddingLeft="10dp"
android:paddingStart="10dp"
android:paddingRight="10dp"
android:paddingEnd="10dp"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:maxLines="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/load_attachment_spoiler" />
</LinearLayout>
<LinearLayout
android:id="@+id/status_content_container"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="wrap_content">
<TextView
android:id="@+id/status_content"
android:layout_marginTop="10dp"
android:autoLink="web"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<LinearLayout
android:orientation="vertical"
android:layout_width="0dp"
android:layout_marginLeft="2dp"
android:layout_marginStart="2dp"
android:id="@+id/status_container2"
android:layout_weight="1"
android:layout_height="match_parent">
android:id="@+id/status_document_container"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="200dp">
<ImageView
android:id="@+id/status_prev2"
android:layout_width="match_parent"
android:id="@+id/status_prev1"
android:layout_width="0dp"
android:layout_weight="1"
android:scaleType="centerCrop"
android:layout_height="0dp"
android:layout_height="match_parent"
tools:ignore="ContentDescription" />
<LinearLayout
android:orientation="vertical"
android:layout_width="0dp"
android:layout_marginLeft="2dp"
android:layout_marginStart="2dp"
android:id="@+id/status_container2"
android:layout_weight="1"
android:layout_width="match_parent"
android:orientation="horizontal"
android:layout_marginTop="2dp"
android:id="@+id/status_container3"
android:layout_height="0dp">
android:layout_height="match_parent">
<ImageView
android:id="@+id/status_prev3"
android:id="@+id/status_prev2"
android:layout_width="match_parent"
android:layout_weight="1"
android:layout_width="0dp"
android:scaleType="centerCrop"
android:layout_height="wrap_content"
android:layout_height="0dp"
tools:ignore="ContentDescription" />
<ImageView
android:layout_marginLeft="2dp"
android:layout_marginStart="2dp"
android:id="@+id/status_prev4"
<LinearLayout
android:layout_weight="1"
android:layout_width="0dp"
android:scaleType="centerCrop"
android:layout_height="wrap_content"
tools:ignore="ContentDescription" />
android:layout_width="match_parent"
android:orientation="horizontal"
android:layout_marginTop="2dp"
android:id="@+id/status_container3"
android:layout_height="0dp">
<ImageView
android:id="@+id/status_prev3"
android:layout_weight="1"
android:layout_width="0dp"
android:scaleType="centerCrop"
android:layout_height="wrap_content"
tools:ignore="ContentDescription" />
<ImageView
android:layout_marginLeft="2dp"
android:layout_marginStart="2dp"
android:id="@+id/status_prev4"
android:layout_weight="1"
android:layout_width="0dp"
android:scaleType="centerCrop"
android:layout_height="wrap_content"
tools:ignore="ContentDescription" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<Button
android:id="@+id/status_show_more"
android:visibility="gone"
android:textAllCaps="false"
android:drawableLeft="@drawable/ic_photo"
android:drawableStart="@drawable/ic_photo"
android:gravity="center"
android:drawablePadding="5dp"
android:paddingLeft="10dp"
android:paddingStart="10dp"
android:paddingRight="10dp"
android:paddingEnd="10dp"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:maxLines="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/load_attachment" />
</LinearLayout>
<Button
android:id="@+id/status_show_more"
android:visibility="gone"
android:textAllCaps="false"
android:drawableLeft="@drawable/ic_photo"
android:drawableStart="@drawable/ic_photo"
android:gravity="center"
android:drawablePadding="5dp"
android:paddingLeft="10dp"
android:paddingStart="10dp"
android:paddingRight="10dp"
android:paddingEnd="10dp"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:maxLines="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/load_attachment" />
</LinearLayout>
</LinearLayout>
<LinearLayout

View File

@ -41,6 +41,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/no_action_text"
android:padding="10dp"
android:gravity="center"
android:textSize="25sp"

View File

@ -22,6 +22,10 @@
android:id="@+id/nav_notification"
android:icon="@drawable/ic_notifications"
android:title="@string/notifications" />
<item
android:id="@+id/nav_follow_request"
android:icon="@drawable/ic_group_add"
android:title="@string/follow_request" />
</group>
</menu>
</item>

View File

@ -21,4 +21,6 @@
<color name="background_image">#282c37</color>
<color name="green_1">#009688</color>
<color name="red_1">#F44336</color>
</resources>

View File

@ -48,6 +48,7 @@
<string name="muted_menu">Utilisateurs muets</string>
<string name="blocked_menu">Utilisateurs bloqués</string>
<string name="notifications">Notifications</string>
<string name="follow_request">Demandes d\'abonnements</string>
<string name="optimization">Optimisation</string>
<string name="profile">Profil</string>
<string name="make_a_choice">Que souhaitez-vous faire ?</string>
@ -134,6 +135,7 @@
<string name="toot_visibility_tilte">Visibilité du pouet</string>
<string name="toot_sent">Le pouet a été envoyé !</string>
<string name="toot_reply_content_title">Vous répondez à ce pouet :</string>
<string name="toot_sensitive">Contenu sensible ?</string>
<string-array name="toot_visibility">
<item>Afficher dans les fils publics</item>
<item>Ne pas afficher dans les fils publics</item>
@ -157,9 +159,12 @@
<!-- Accounts -->
<string name="no_accounts">Aucun compte à afficher</string>
<string name="no_follow_request">Aucune demande d\'abonnement</string>
<string name="status_cnt">Pouets \n %d</string>
<string name="following_cnt">Abonnements \n %d</string>
<string name="followers_cnt">Abonnés \n %d</string>
<string name="authorize">Autoriser</string>
<string name="reject">Rejeter</string>
<!-- Notifications -->
<string name="no_notifications">Aucune notification à afficher</string>
@ -197,8 +202,10 @@
<string name="toast_unstatus">Le pouet a été supprimé !</string>
<string name="toast_error">Oups ! Une erreur s\'est produite !</string>
<string name="toast_error_loading_account">Une erreur s\'est produite en chargeant le compte !</string>
<string name="toast_error_search">Une erreur s\'est produite lors de la recherche !</string>
<string name="toast_error_login">Impossible de vous connecter !</string>
<string name="toast_update_credential_ok">Les données du profil ont été sauvegardées !</string>
<string name="nothing_to_do">Aucune action ne peut être réalisée</string>
<!-- Settings -->
<string name="settings_title_optimisation">Optimisation du chargement</string>
<string name="set_toots_page">Nombre de pouets par chargement</string>
@ -209,6 +216,8 @@
<string name="set_attachment_ask">Demander</string>
<string name="set_attachment_action">Charger les médias</string>
<string name="load_attachment">Charger les images</string>
<string name="load_attachment_spoiler">Afficher le contenu ?</string>
<string name="load_sensitive_attachment">Charger les images sensibles</string>
<string name="set_display_reply">Afficher le message précédent lors d\'une réponse</string>
<string name="settings_title_notifications">Gestion des notifications</string>
@ -237,6 +246,7 @@
<string name="action_block">Bloquer</string>
<string name="action_unblock">Débloquer</string>
<string name="action_mute">Masquer</string>
<string name="action_no_action">Aucune action</string>
<string name="action_unmute">Afficher</string>
<string name="request_sent">Demande envoyée</string>
<string name="followed_by">Vous suit</string>
@ -269,6 +279,7 @@
L\'utilisation de bibliothèques est réduite au strict minimum :\n
- <b>Android Asynchronous Http Client</b> : Pour la gestion des requêtes\n
- <b>Universal Image Loader</b> : Pour la gestion des médias\n
- <b>Android-Job</b> : Pour la gestion des services
- <b>Android-Job</b> : Pour la gestion des services\n
- <b>Emoji-java</b> : Pour l\'affichage des emoji
</string>
</resources>