diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/activities/LoginActivity.java b/app/src/main/java/fr/gouv/etalab/mastodon/activities/LoginActivity.java index 080db6d86..27fc0ddb2 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/activities/LoginActivity.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/activities/LoginActivity.java @@ -14,16 +14,20 @@ * see . */ package fr.gouv.etalab.mastodon.activities; +import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.graphics.Paint; import android.os.AsyncTask; import android.os.Bundle; +import android.support.design.widget.NavigationView; import android.support.v7.app.AppCompatActivity; +import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.EditText; +import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; @@ -60,13 +64,17 @@ public class LoginActivity extends AppCompatActivity { private String client_secret; private TextView login_two_step; private static boolean client_id_for_webview = false; + private String instance; + private boolean addAccount = false; + private EditText login_instance; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); - Button connectionButton = (Button) findViewById(R.id.login_button); + final Button connectionButton = (Button) findViewById(R.id.login_button); + login_instance = (EditText) findViewById(R.id.login_instance); connectionButton.setEnabled(false); login_two_step = (TextView) findViewById(R.id.login_two_step); login_two_step.setVisibility(View.GONE); @@ -78,28 +86,61 @@ public class LoginActivity extends AppCompatActivity { retrievesClientId(); } }); + + Bundle b = getIntent().getExtras(); + if(b != null) + addAccount = b.getBoolean("addAccount", false); + + if( addAccount ) + login_instance.setVisibility(View.VISIBLE); + + if( addAccount) { + login_instance.setOnFocusChangeListener(new View.OnFocusChangeListener() { + @Override + public void onFocusChange(View v, boolean hasFocus) { + connectionButton.setEnabled(false); + login_two_step.setVisibility(View.INVISIBLE); + if (!hasFocus) { + retrievesClientId(); + } + } + }); + } } @Override protected void onResume(){ super.onResume(); Button connectionButton = (Button) findViewById(R.id.login_button); - if( client_id_for_webview || !connectionButton.isEnabled()) { - connectionButton.setEnabled(false); - client_id_for_webview = false; - retrievesClientId(); + if( !addAccount ) { + if (client_id_for_webview || !connectionButton.isEnabled()) { + connectionButton.setEnabled(false); + client_id_for_webview = false; + retrievesClientId(); + } + }else { + if (login_instance.getText() != null && login_instance.getText().toString().length() > 0 && client_id_for_webview) { + connectionButton.setEnabled(false); + client_id_for_webview = false; + retrievesClientId(); + } } } private void retrievesClientId(){ final Button connectionButton = (Button) findViewById(R.id.login_button); + if( login_instance.getText() != null && login_instance.getText().length() > 0 ) + instance = login_instance.getText().toString().trim(); + else + instance = Helper.INSTANCE; + String action = "/api/v1/apps"; RequestParams parameters = new RequestParams(); parameters.add(Helper.CLIENT_NAME, Helper.OAUTH_REDIRECT_HOST); parameters.add(Helper.REDIRECT_URIS, client_id_for_webview?Helper.REDIRECT_CONTENT_WEB:Helper.REDIRECT_CONTENT); parameters.add(Helper.SCOPES, Helper.OAUTH_SCOPES); - parameters.add(Helper.WEBSITE,"https://" + Helper.INSTANCE); - new OauthClient().post(action, parameters, new AsyncHttpResponseHandler() { + parameters.add(Helper.WEBSITE,"https://" + Helper.getLiveInstance(getApplicationContext())); + new OauthClient(instance).post(action, parameters, new AsyncHttpResponseHandler() { @Override public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) { String response = new String(responseBody); @@ -120,6 +161,7 @@ public class LoginActivity extends AppCompatActivity { login_two_step.setVisibility(View.VISIBLE); if( client_id_for_webview){ Intent i = new Intent(LoginActivity.this, WebviewActivity.class); + i.putExtra("instance", instance); startActivity(i); } } catch (JSONException e) { @@ -158,7 +200,7 @@ public class LoginActivity extends AppCompatActivity { client.setUserAgent(USER_AGENT); try { client.setSSLSocketFactory(new MastalabSSLSocketFactory(MastalabSSLSocketFactory.getKeystore())); - client.post("https://" + Helper.INSTANCE + "/oauth/token", requestParams, new AsyncHttpResponseHandler() { + client.post("https://" + instance+ "/oauth/token", requestParams, new AsyncHttpResponseHandler() { @Override public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) { String response = new String(responseBody); @@ -171,7 +213,7 @@ public class LoginActivity extends AppCompatActivity { editor.putString(Helper.PREF_KEY_OAUTH_TOKEN, token); editor.apply(); //Update the account with the token; - new UpdateAccountInfoAsyncTask(LoginActivity.this, token).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + new UpdateAccountInfoAsyncTask(LoginActivity.this, token, instance).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } catch (JSONException e) { e.printStackTrace(); } @@ -180,6 +222,7 @@ public class LoginActivity extends AppCompatActivity { @Override public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) { connectionButton.setEnabled(true); + error.printStackTrace(); Toast.makeText(getApplicationContext(),R.string.toast_error_login,Toast.LENGTH_LONG).show(); } }); @@ -192,4 +235,7 @@ public class LoginActivity extends AppCompatActivity { }); } + + + } \ No newline at end of file diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/activities/MainActivity.java b/app/src/main/java/fr/gouv/etalab/mastodon/activities/MainActivity.java index 25c56f7e6..b7e7f99a3 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/activities/MainActivity.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/activities/MainActivity.java @@ -35,10 +35,7 @@ import android.view.Menu; import android.view.MenuItem; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; -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; @@ -67,6 +64,8 @@ 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.menuAccounts; +import static fr.gouv.etalab.mastodon.helper.Helper.updateHeaderAccountInfo; public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, OnUpdateAccountInfoInterface { @@ -137,7 +136,16 @@ public class MainActivity extends AppCompatActivity SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); String prefKeyOauthTokenT = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null); Account account = new AccountDAO(getApplicationContext(), db).getAccountByToken(prefKeyOauthTokenT); - updateHeaderAccountInfo(account); + updateHeaderAccountInfo(MainActivity.this, account, headerLayout, imageLoader, options); + + LinearLayout owner_container = (LinearLayout) headerLayout.findViewById(R.id.owner_container); + owner_container.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + menuAccounts(MainActivity.this); + } + }); + boolean menuWasSelected = false; if( getIntent() != null && getIntent().getExtras() != null ){ Bundle extras = getIntent().getExtras(); @@ -436,29 +444,6 @@ public class MainActivity extends AppCompatActivity return true; } - private void updateHeaderAccountInfo(Account account){ - ImageView profilePicture = (ImageView) headerLayout.findViewById(R.id.profilePicture); - TextView username = (TextView) headerLayout.findViewById(R.id.username); - TextView displayedName = (TextView) headerLayout.findViewById(R.id.displayedName); - 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); - //Something wrong happened with the account recorded in db (ie: bad token) - if( account == null ) { - Helper.logout(getApplicationContext()); - Intent myIntent = new Intent(MainActivity.this, LoginActivity.class); - Toast.makeText(getApplicationContext(),R.string.toast_error, Toast.LENGTH_LONG).show(); - startActivity(myIntent); - finish(); //User is logged out to get a new token - }else { - ownerStatus.setText(String.valueOf(account.getStatuses_count())); - ownerFollowers.setText(String.valueOf(account.getFollowers_count())); - ownerFollowing.setText(String.valueOf(account.getFollowing_count())); - username.setText(String.format("@%s",account.getUsername())); - displayedName.setText(account.getDisplay_name()); - imageLoader.displayImage(account.getAvatar(), profilePicture, options); - } - } private void populateTitleWithTag(String tag, String title, int index){ if( tag == null) @@ -488,7 +473,7 @@ public class MainActivity extends AppCompatActivity String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); SQLiteDatabase db = Sqlite.getInstance(MainActivity.this, Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); Account account = new AccountDAO(getApplicationContext(), db).getAccountByID(userId); - updateHeaderAccountInfo(account); + updateHeaderAccountInfo(MainActivity.this, account, headerLayout, imageLoader, options); } } } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/activities/WebviewActivity.java b/app/src/main/java/fr/gouv/etalab/mastodon/activities/WebviewActivity.java index 952c923cb..fb8549a27 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/activities/WebviewActivity.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/activities/WebviewActivity.java @@ -53,11 +53,18 @@ public class WebviewActivity extends AppCompatActivity { private WebView webView; private AlertDialog alert; private String clientId, clientSecret; + private String instance; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_webview); + Bundle b = getIntent().getExtras(); + if(b != null) + instance = b.getString("instance"); + if( instance == null) + finish(); + SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); clientId = sharedpreferences.getString(Helper.CLIENT_ID, null); clientSecret = sharedpreferences.getString(Helper.CLIENT_SECRET, null); @@ -95,7 +102,7 @@ public class WebviewActivity extends AppCompatActivity { parameters.add(Helper.REDIRECT_URI,Helper.REDIRECT_CONTENT_WEB); parameters.add("grant_type", "authorization_code"); parameters.add("code",code); - new OauthClient().post(action, parameters, new AsyncHttpResponseHandler() { + new OauthClient(instance).post(action, parameters, new AsyncHttpResponseHandler() { @Override public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) { String response = new String(responseBody); @@ -108,7 +115,7 @@ public class WebviewActivity extends AppCompatActivity { editor.putString(Helper.PREF_KEY_OAUTH_TOKEN, token); editor.apply(); //Update the account with the token; - new UpdateAccountInfoAsyncTask(WebviewActivity.this, token).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + new UpdateAccountInfoAsyncTask(WebviewActivity.this, token, instance).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } catch (JSONException e) { e.printStackTrace(); } @@ -149,7 +156,7 @@ public class WebviewActivity extends AppCompatActivity { queryString += "&" + Helper.REDIRECT_URI + "="+ Uri.encode(Helper.REDIRECT_CONTENT_WEB); queryString += "&" + Helper.RESPONSE_TYPE +"=code"; queryString += "&" + Helper.SCOPE +"=" + Helper.OAUTH_SCOPES; - return "https://" + Helper.INSTANCE + Helper.EP_AUTHORIZE + "?" + queryString; + return "https://" + instance + Helper.EP_AUTHORIZE + "?" + queryString; } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RemoveAccountAsyncTask.java b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RemoveAccountAsyncTask.java new file mode 100644 index 000000000..03d68173b --- /dev/null +++ b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RemoveAccountAsyncTask.java @@ -0,0 +1,47 @@ +/* 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 . */ +package fr.gouv.etalab.mastodon.asynctasks; + +import android.app.Activity; +import android.database.sqlite.SQLiteDatabase; +import android.os.AsyncTask; +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 28/05/2017. + * Remove an account in db + */ + +public class RemoveAccountAsyncTask extends AsyncTask { + + private Activity activity; + private Account account; + + public RemoveAccountAsyncTask(Activity activity, Account account){ + this.activity = activity; + this.account = account; + } + + @Override + protected Void doInBackground(Void... params) { + SQLiteDatabase db = Sqlite.getInstance(activity, Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + new AccountDAO(activity, db).removeUser(account); + return null; + } + + +} \ No newline at end of file diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveHomeTimelineServiceAsyncTask.java b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveHomeTimelineServiceAsyncTask.java index 1abc000f3..89a6c6b7c 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveHomeTimelineServiceAsyncTask.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/RetrieveHomeTimelineServiceAsyncTask.java @@ -34,18 +34,19 @@ public class RetrieveHomeTimelineServiceAsyncTask extends AsyncTask private String max_id; private String acct; private OnRetrieveNotificationsInterface listener; - + private String instance; public RetrieveNotificationsAsyncTask(Context context, String max_id, String acct, OnRetrieveNotificationsInterface onRetrieveNotificationsInterface){ this.context = context; @@ -45,12 +45,21 @@ public class RetrieveNotificationsAsyncTask extends AsyncTask this.acct = acct; } + + public RetrieveNotificationsAsyncTask(Context context, String instance, String max_id, String acct, OnRetrieveNotificationsInterface onRetrieveNotificationsInterface){ + this.context = context; + this.max_id = max_id; + this.listener = onRetrieveNotificationsInterface; + this.acct = acct; + this.instance = instance; + } + @Override protected Void doInBackground(Void... params) { if( acct == null) - notifications = new API(context).getNotifications(max_id); + notifications = new API(context, instance).getNotifications(max_id); else - notifications = new API(context).getNotificationsSince(max_id); + notifications = new API(context, instance).getNotificationsSince(max_id); return null; } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/UpdateAccountInfoAsyncTask.java b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/UpdateAccountInfoAsyncTask.java index b482cffbf..84057d894 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/UpdateAccountInfoAsyncTask.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/asynctasks/UpdateAccountInfoAsyncTask.java @@ -20,6 +20,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.database.sqlite.SQLiteDatabase; import android.os.AsyncTask; +import android.util.Log; import fr.gouv.etalab.mastodon.activities.MainActivity; import fr.gouv.etalab.mastodon.client.API; @@ -37,23 +38,24 @@ public class UpdateAccountInfoAsyncTask extends AsyncTask { private Context context; private String token; + private String instance; - public UpdateAccountInfoAsyncTask(Context context, String token){ + public UpdateAccountInfoAsyncTask(Context context, String token, String instance){ this.context = context; this.token = token; - + this.instance = instance; } @Override protected Void doInBackground(Void... params) { - Account account = new API(context).verifyCredentials(); + Account account = new API(context, instance).verifyCredentials(); SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); if( token == null) { token = sharedpreferences.getString(Helper.PREF_KEY_OAUTH_TOKEN, null); } account.setToken(token); //TODO: remove this static value to allow other instances - account.setInstance(Helper.INSTANCE); + account.setInstance(instance); SQLiteDatabase db = Sqlite.getInstance(context, Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); boolean userExists = new AccountDAO(context, db).userExist(account); SharedPreferences.Editor editor = sharedpreferences.edit(); diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/client/API.java b/app/src/main/java/fr/gouv/etalab/mastodon/client/API.java index fd074721b..10b68f107 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/client/API.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/client/API.java @@ -59,7 +59,7 @@ import static fr.gouv.etalab.mastodon.helper.Helper.USER_AGENT; public class API { - private static final String BASE_URL = "https://" + Helper.INSTANCE + "/api/v1"; + private SyncHttpClient client = new SyncHttpClient(); @@ -74,6 +74,7 @@ public class API { private List notifications; private int tootPerPage, accountPerPage, notificationPerPage; private int actionCode; + private String instance; public enum StatusAction{ FAVOURITE, @@ -97,8 +98,22 @@ public class API { tootPerPage = sharedpreferences.getInt(Helper.SET_TOOTS_PER_PAGE, 40); accountPerPage = sharedpreferences.getInt(Helper.SET_ACCOUNTS_PER_PAGE, 40); notificationPerPage = sharedpreferences.getInt(Helper.SET_NOTIFICATIONS_PER_PAGE, 40); + this.instance = Helper.getLiveInstance(context); } + public API(Context context, String instance) { + this.context = context; + SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + tootPerPage = sharedpreferences.getInt(Helper.SET_TOOTS_PER_PAGE, 40); + accountPerPage = sharedpreferences.getInt(Helper.SET_ACCOUNTS_PER_PAGE, 40); + notificationPerPage = sharedpreferences.getInt(Helper.SET_NOTIFICATIONS_PER_PAGE, 40); + if( instance != null) + this.instance = instance; + else + this.instance = Helper.getLiveInstance(context); + } + + /*** * Verifiy credential of the authenticated user *synchronously* * @return Account @@ -1215,6 +1230,7 @@ public class API { client.addHeader("Authorization", "Bearer "+prefKeyOauthTokenT); client.setSSLSocketFactory(new MastalabSSLSocketFactory(MastalabSSLSocketFactory.getKeystore())); client.get(getAbsoluteUrl(action), params, responseHandler); + } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException | UnrecoverableKeyException e) { Toast.makeText(context, R.string.toast_error,Toast.LENGTH_LONG).show(); e.printStackTrace(); @@ -1252,10 +1268,10 @@ public class API { } } + private String getAbsoluteUrl(String action) { - return BASE_URL + action; + return "https://" + this.instance + "/api/v1" + action; } - } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/client/OauthClient.java b/app/src/main/java/fr/gouv/etalab/mastodon/client/OauthClient.java index 77275279e..9b5f66c38 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/client/OauthClient.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/client/OauthClient.java @@ -35,9 +35,13 @@ import static fr.gouv.etalab.mastodon.helper.Helper.USER_AGENT; public class OauthClient { - private static final String BASE_URL = "https://" + Helper.INSTANCE; - private static AsyncHttpClient client = new AsyncHttpClient(); + private String instance; + + + public OauthClient(String instance){ + this.instance = instance; + } public void get(String action, RequestParams params, AsyncHttpResponseHandler responseHandler) { try { @@ -63,7 +67,7 @@ public class OauthClient { } private String getAbsoluteUrl(String action) { - return BASE_URL + action; + return "https://" + instance + action; } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/helper/Helper.java b/app/src/main/java/fr/gouv/etalab/mastodon/helper/Helper.java index d157c8252..08ed3caab 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/helper/Helper.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/helper/Helper.java @@ -17,6 +17,7 @@ package fr.gouv.etalab.mastodon.helper; +import android.app.Activity; import android.app.AlertDialog; import android.app.DownloadManager; import android.app.PendingIntent; @@ -25,30 +26,56 @@ import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Resources; +import android.database.sqlite.SQLiteDatabase; import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import android.media.RingtoneManager; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; +import android.os.AsyncTask; import android.os.Build; +import android.os.Bundle; import android.os.Environment; +import android.support.design.widget.NavigationView; import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationManagerCompat; import android.util.DisplayMetrics; +import android.util.Log; +import android.view.MenuItem; +import android.view.View; import android.view.WindowManager; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; import android.widget.Toast; import com.loopj.android.http.BuildConfig; +import com.nostra13.universalimageloader.core.DisplayImageOptions; +import com.nostra13.universalimageloader.core.ImageLoader; +import com.nostra13.universalimageloader.core.assist.FailReason; +import com.nostra13.universalimageloader.core.display.RoundedBitmapDisplayer; +import com.nostra13.universalimageloader.core.display.SimpleBitmapDisplayer; +import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; import java.io.File; import java.net.InetAddress; import java.text.ParseException; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Date; +import java.util.List; import java.util.Locale; import java.util.TimeZone; +import fr.gouv.etalab.mastodon.activities.LoginActivity; import fr.gouv.etalab.mastodon.activities.MainActivity; +import fr.gouv.etalab.mastodon.asynctasks.RemoveAccountAsyncTask; +import fr.gouv.etalab.mastodon.client.Entities.Account; +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.API; @@ -134,6 +161,9 @@ public class Helper { //User agent public static final String USER_AGENT = "Mastalab/"+ BuildConfig.VERSION_NAME + " Android/"+ Build.VERSION.RELEASE; + + public static boolean menuAccountsOpened = false; + /*** * Check if the user is connected to Internet * @return boolean @@ -417,10 +447,207 @@ public class Helper { notificationManager.notify(notificationId, notificationBuilder.build()); } + /** + * Returns the instance of the authenticated user + * @param context Context + * @return String domain instance + */ + public static String getLiveInstance(Context context){ + final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + SQLiteDatabase db = Sqlite.getInstance(context, Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); + if( userId == null) //User not authenticated + return Helper.INSTANCE; + Account account = new AccountDAO(context, db).getAccountByID(userId); + if( account != null){ + return account.getInstance().trim(); + } //User not in db + else return Helper.INSTANCE; + } + + /** + * Converts dp to pixel + * @param dp float - the value in dp to convert + * @param context Context + * @return float - the converted value in pixel + */ public static float convertDpToPixel(float dp, Context context){ Resources resources = context.getResources(); DisplayMetrics metrics = resources.getDisplayMetrics(); return dp * ((float)metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT); } + + + /** + * Toggle for the menu (ie: main menu or accounts menu) + * @param activity Activity + */ + public static void menuAccounts(final Activity activity){ + + final NavigationView navigationView = (NavigationView) activity.findViewById(R.id.nav_view); + SharedPreferences mSharedPreferences = activity.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + String currrentUserId = mSharedPreferences.getString(Helper.PREF_KEY_ID, null); + final ImageView arrow = (ImageView) navigationView.getHeaderView(0).findViewById(R.id.owner_accounts); + if( currrentUserId == null) + return; + + if( !menuAccountsOpened ){ + + arrow.setImageResource(R.drawable.ic_arrow_drop_up); + + navigationView.getMenu().clear(); + navigationView.inflateMenu(R.menu.menu_accounts); + + SQLiteDatabase db = Sqlite.getInstance(activity, Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + + final List accounts = new AccountDAO(activity, db).getAllAccount(); + navigationView.setItemIconTintList(null); + for(final Account account: accounts) { + if( !currrentUserId.equals(account.getId()) ) { + final MenuItem item = navigationView.getMenu().add("@" + account.getAcct() + "@" + account.getInstance()); + ImageLoader imageLoader; + DisplayImageOptions options = new DisplayImageOptions.Builder().displayer(new SimpleBitmapDisplayer()).cacheInMemory(false) + .cacheOnDisk(true).resetViewBeforeLoading(true).build(); + imageLoader = ImageLoader.getInstance(); + final ImageView imageView = new ImageView(activity); + item.setIcon(R.drawable.ic_person); + imageLoader.displayImage(account.getAvatar(), imageView, options, new ImageLoadingListener() { + @Override + public void onLoadingStarted(String s, View view) { + } + + @Override + public void onLoadingFailed(String s, View view, FailReason failReason) { + } + + @Override + public void onLoadingComplete(String s, View view, Bitmap bitmap) { + item.setIcon(new BitmapDrawable(activity.getResources(), bitmap)); + } + + @Override + public void onLoadingCancelled(String s, View view) { + } + }); + + item.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + if( ! activity.isFinishing() ) { + menuAccountsOpened = false; + String userId = account.getId(); + 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; + } + }); + item.setActionView(R.layout.update_account); + ImageView deleteButton = (ImageView) item.getActionView().findViewById(R.id.account_remove_button); + deleteButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + new AlertDialog.Builder(activity) + .setTitle(activity.getString(R.string.delete_account_title)) + .setMessage(activity.getString(R.string.delete_account_message, "@" + account.getAcct() + "@" + account.getInstance())) + .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + new RemoveAccountAsyncTask(activity, account).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + item.setVisible(false); + } + }) + .setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + // do nothing + } + }) + .setIcon(android.R.drawable.ic_dialog_alert) + .show(); + } + }); + + } + } + MenuItem addItem = navigationView.getMenu().add(R.string.add_account); + addItem.setIcon(R.drawable.ic_person_add); + addItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(MenuItem item) { + Intent intent = new Intent(activity, LoginActivity.class); + intent.putExtra("addAccount", true); + activity.startActivity(intent); + return true; + } + }); + }else{ + arrow.setImageResource(R.drawable.ic_arrow_drop_down); + navigationView.getMenu().clear(); + navigationView.inflateMenu(R.menu.activity_main_drawer); + + } + menuAccountsOpened = !menuAccountsOpened; + + } + + /** + * Changes the user in shared preferences + * @param activity Activity + * @param userID String - the new user id + */ + private static void changeUser(Activity activity, String userID) { + + SQLiteDatabase db = Sqlite.getInstance(activity, Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + Account account = new AccountDAO(activity,db).getAccountByID(userID); + + SharedPreferences sharedpreferences = activity.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedpreferences.edit(); + editor.putString(Helper.PREF_KEY_OAUTH_TOKEN, account.getToken()); + editor.putString(Helper.PREF_KEY_ID, account.getId()); + editor.apply(); + ImageLoader imageLoader; + 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); + updateHeaderAccountInfo(activity, account, navigationView, imageLoader, options); + } + + + /** + * Update the header with the new selected account + * @param activity Activity + * @param account Account - new account in use + * @param headerLayout View - the menu header + * @param imageLoader ImageLoader - instance of ImageLoader + * @param options DisplayImageOptions - current configuration of ImageLoader + */ + public static void updateHeaderAccountInfo(Activity activity, Account account, View headerLayout, ImageLoader imageLoader, DisplayImageOptions options){ + ImageView profilePicture = (ImageView) headerLayout.findViewById(R.id.profilePicture); + TextView username = (TextView) headerLayout.findViewById(R.id.username); + TextView displayedName = (TextView) headerLayout.findViewById(R.id.displayedName); + 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); + if( account == null ) { + Helper.logout(activity); + Intent myIntent = new Intent(activity, LoginActivity.class); + Toast.makeText(activity,R.string.toast_error, Toast.LENGTH_LONG).show(); + activity.startActivity(myIntent); + activity.finish(); //User is logged out to get a new token + }else { + ownerStatus.setText(String.valueOf(account.getStatuses_count())); + ownerFollowers.setText(String.valueOf(account.getFollowers_count())); + ownerFollowing.setText(String.valueOf(account.getFollowing_count())); + username.setText(String.format("@%s",account.getUsername())); + displayedName.setText(account.getDisplay_name()); + imageLoader.displayImage(account.getAvatar(), profilePicture, options); + } + } } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/jobs/HomeTimelineSyncJob.java b/app/src/main/java/fr/gouv/etalab/mastodon/jobs/HomeTimelineSyncJob.java index 743371932..0e914fa42 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/jobs/HomeTimelineSyncJob.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/jobs/HomeTimelineSyncJob.java @@ -104,7 +104,7 @@ public class HomeTimelineSyncJob extends Job implements OnRetrieveHomeTimelineSe for (Account account: accounts) { String since_id = sharedpreferences.getString(Helper.LAST_HOMETIMELINE_MAX_ID + account.getAcct(), null); notificationId = (int) Math.round(Double.parseDouble(account.getId())/1000); - new RetrieveHomeTimelineServiceAsyncTask(getContext(), since_id, account.getAcct(), HomeTimelineSyncJob.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + new RetrieveHomeTimelineServiceAsyncTask(getContext(), account.getInstance(), since_id, account.getAcct(), HomeTimelineSyncJob.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/jobs/NotificationsSyncJob.java b/app/src/main/java/fr/gouv/etalab/mastodon/jobs/NotificationsSyncJob.java index 84510a7e1..0f747bdc1 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/jobs/NotificationsSyncJob.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/jobs/NotificationsSyncJob.java @@ -114,7 +114,7 @@ public class NotificationsSyncJob extends Job implements OnRetrieveNotifications for (Account account: accounts) { String max_id = sharedpreferences.getString(Helper.LAST_NOTIFICATION_MAX_ID + account.getAcct(), null); notificationId = (int) Math.round(Double.parseDouble(account.getId())/1000); - new RetrieveNotificationsAsyncTask(getContext(), max_id, account.getAcct(), NotificationsSyncJob.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + new RetrieveNotificationsAsyncTask(getContext(), account.getInstance(), max_id, account.getAcct(), NotificationsSyncJob.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } } } diff --git a/app/src/main/java/fr/gouv/etalab/mastodon/sqlite/AccountDAO.java b/app/src/main/java/fr/gouv/etalab/mastodon/sqlite/AccountDAO.java index 3a75885c4..a5b7a6c09 100644 --- a/app/src/main/java/fr/gouv/etalab/mastodon/sqlite/AccountDAO.java +++ b/app/src/main/java/fr/gouv/etalab/mastodon/sqlite/AccountDAO.java @@ -76,6 +76,7 @@ public class AccountDAO { db.insert(Sqlite.TABLE_USER_ACCOUNT, null, values); }catch (Exception e) { + e.printStackTrace(); return false; } return true; diff --git a/app/src/main/res/drawable-hdpi/ic_arrow_drop_down.png b/app/src/main/res/drawable-hdpi/ic_arrow_drop_down.png new file mode 100644 index 000000000..43666f202 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_arrow_drop_down.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_arrow_drop_up.png b/app/src/main/res/drawable-hdpi/ic_arrow_drop_up.png new file mode 100644 index 000000000..4198b7669 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_arrow_drop_up.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_cancel.png b/app/src/main/res/drawable-hdpi/ic_cancel.png new file mode 100644 index 000000000..e25273a14 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_cancel.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_person.png b/app/src/main/res/drawable-hdpi/ic_person.png new file mode 100644 index 000000000..ae93a3f05 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_person.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_person_add.png b/app/src/main/res/drawable-hdpi/ic_person_add.png new file mode 100644 index 000000000..347ab6922 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_person_add.png differ diff --git a/app/src/main/res/drawable-ldpi/ic_arrow_drop_down.png b/app/src/main/res/drawable-ldpi/ic_arrow_drop_down.png new file mode 100644 index 000000000..5a20eb61f Binary files /dev/null and b/app/src/main/res/drawable-ldpi/ic_arrow_drop_down.png differ diff --git a/app/src/main/res/drawable-ldpi/ic_arrow_drop_up.png b/app/src/main/res/drawable-ldpi/ic_arrow_drop_up.png new file mode 100644 index 000000000..73812a56a Binary files /dev/null and b/app/src/main/res/drawable-ldpi/ic_arrow_drop_up.png differ diff --git a/app/src/main/res/drawable-ldpi/ic_cancel.png b/app/src/main/res/drawable-ldpi/ic_cancel.png new file mode 100644 index 000000000..3b95a3913 Binary files /dev/null and b/app/src/main/res/drawable-ldpi/ic_cancel.png differ diff --git a/app/src/main/res/drawable-ldpi/ic_person.png b/app/src/main/res/drawable-ldpi/ic_person.png new file mode 100644 index 000000000..5985e8931 Binary files /dev/null and b/app/src/main/res/drawable-ldpi/ic_person.png differ diff --git a/app/src/main/res/drawable-ldpi/ic_person_add.png b/app/src/main/res/drawable-ldpi/ic_person_add.png new file mode 100644 index 000000000..d92cb4412 Binary files /dev/null and b/app/src/main/res/drawable-ldpi/ic_person_add.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_arrow_drop_down.png b/app/src/main/res/drawable-mdpi/ic_arrow_drop_down.png new file mode 100644 index 000000000..1d9b140a7 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_arrow_drop_down.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_arrow_drop_up.png b/app/src/main/res/drawable-mdpi/ic_arrow_drop_up.png new file mode 100644 index 000000000..54989ad3b Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_arrow_drop_up.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_cancel.png b/app/src/main/res/drawable-mdpi/ic_cancel.png new file mode 100644 index 000000000..ff5bd2973 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_cancel.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_person.png b/app/src/main/res/drawable-mdpi/ic_person.png new file mode 100644 index 000000000..38b67d989 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_person.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_person_add.png b/app/src/main/res/drawable-mdpi/ic_person_add.png new file mode 100644 index 000000000..397df9747 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_person_add.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_arrow_drop_down.png b/app/src/main/res/drawable-xhdpi/ic_arrow_drop_down.png new file mode 100644 index 000000000..9af517d9c Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_arrow_drop_down.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_arrow_drop_up.png b/app/src/main/res/drawable-xhdpi/ic_arrow_drop_up.png new file mode 100644 index 000000000..3f0c3cdff Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_arrow_drop_up.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_cancel.png b/app/src/main/res/drawable-xhdpi/ic_cancel.png new file mode 100644 index 000000000..8a3c23efc Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_cancel.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_person.png b/app/src/main/res/drawable-xhdpi/ic_person.png new file mode 100644 index 000000000..4d439e6a2 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_person.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_person_add.png b/app/src/main/res/drawable-xhdpi/ic_person_add.png new file mode 100644 index 000000000..c272ad5b4 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_person_add.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_arrow_drop_down.png b/app/src/main/res/drawable-xxhdpi/ic_arrow_drop_down.png new file mode 100644 index 000000000..3a5f84646 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_arrow_drop_down.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_arrow_drop_up.png b/app/src/main/res/drawable-xxhdpi/ic_arrow_drop_up.png new file mode 100644 index 000000000..95cde7b20 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_arrow_drop_up.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_cancel.png b/app/src/main/res/drawable-xxhdpi/ic_cancel.png new file mode 100644 index 000000000..aeb96f0c9 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_cancel.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_person.png b/app/src/main/res/drawable-xxhdpi/ic_person.png new file mode 100644 index 000000000..c1e75b4d1 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_person.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_person_add.png b/app/src/main/res/drawable-xxhdpi/ic_person_add.png new file mode 100644 index 000000000..d122edbb1 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_person_add.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_arrow_drop_down.png b/app/src/main/res/drawable-xxxhdpi/ic_arrow_drop_down.png new file mode 100644 index 000000000..452e50216 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_arrow_drop_down.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_arrow_drop_up.png b/app/src/main/res/drawable-xxxhdpi/ic_arrow_drop_up.png new file mode 100644 index 000000000..45ea95ed6 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_arrow_drop_up.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_cancel.png b/app/src/main/res/drawable-xxxhdpi/ic_cancel.png new file mode 100644 index 000000000..5cb6fbb47 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_cancel.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_person.png b/app/src/main/res/drawable-xxxhdpi/ic_person.png new file mode 100644 index 000000000..940aa754a Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_person.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_person_add.png b/app/src/main/res/drawable-xxxhdpi/ic_person_add.png new file mode 100644 index 000000000..81954c033 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_person_add.png differ diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index 53a6e9d1d..06bfd9422 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -37,6 +37,17 @@ android:gravity="center" android:orientation="vertical" android:layout_height="wrap_content"> + + + + - + android:orientation="horizontal"> + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_accounts.xml b/app/src/main/res/menu/menu_accounts.xml new file mode 100644 index 000000000..891ed2c9a --- /dev/null +++ b/app/src/main/res/menu/menu_accounts.xml @@ -0,0 +1,9 @@ + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8166e0151..04dac5c29 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -24,6 +24,9 @@ Jeton Authentification en deux étapes ? Aucun résultat ! + Instance + Utilisation du compte %1$s + Ajouter un compte Accueil Accueil @@ -39,6 +42,8 @@ Notifications Optimisation Que souhaitez-vous faire ? + Supprimer un compte + Supprimer le compte %1$s de l\'application ? Aucun pouet à afficher !