package app.fedilab.fedilabtube; import android.content.Intent; import android.content.SharedPreferences; import android.database.sqlite.SQLiteDatabase; import android.os.AsyncTask; import android.os.Bundle; import android.text.SpannableString; import android.text.Spanned; import android.text.style.ForegroundColorSpan; import android.text.style.UnderlineSpan; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; import org.jetbrains.annotations.NotNull; import org.json.JSONException; import org.json.JSONObject; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import app.fedilab.fedilabtube.asynctasks.RetrievePeertubeNotificationsAsyncTask; import app.fedilab.fedilabtube.asynctasks.UpdateAccountInfoAsyncTask; import app.fedilab.fedilabtube.client.APIResponse; import app.fedilab.fedilabtube.client.HttpsConnection; import app.fedilab.fedilabtube.client.entities.Account; import app.fedilab.fedilabtube.client.entities.PeertubeNotification; import app.fedilab.fedilabtube.drawer.PeertubeNotificationsListAdapter; import app.fedilab.fedilabtube.helper.Helper; import app.fedilab.fedilabtube.interfaces.OnRetrievePeertubeNotificationsInterface; import app.fedilab.fedilabtube.sqlite.AccountDAO; import app.fedilab.fedilabtube.sqlite.Sqlite; import es.dmoral.toasty.Toasty; public class LoginActivity extends AppCompatActivity implements OnRetrievePeertubeNotificationsInterface { //Peertube notification type public static int NEW_VIDEO_FROM_SUBSCRIPTION = 1; // public static int NEW_COMMENT_ON_MY_VIDEO = 2; // public static int NEW_VIDEO_ABUSE_FOR_MODERATORS = 3; public static int BLACKLIST_ON_MY_VIDEO = 4; public static int UNBLACKLIST_ON_MY_VIDEO = 5; public static int MY_VIDEO_PUBLISHED = 6; public static int MY_VIDEO_IMPORT_SUCCESS = 7; public static int MY_VIDEO_IMPORT_ERROR = 8; private static String client_id; private static String client_secret; // public static int NEW_USER_REGISTRATION = 9; //public static int NEW_FOLLOW = 10; // public static int COMMENT_MENTION = 11; LinearLayoutManager mLayoutManager; private EditText login_uid; private EditText login_passwd; private Button connectionButton; private String actionToken; private boolean flag_loading; private PeertubeNotificationsListAdapter notificationsListAdapter; private String max_id; private List notifications; private RelativeLayout mainLoader, nextElementLoader, textviewNoAction; private boolean firstLoad; private SwipeRefreshLayout swipeRefreshLayout; private boolean swiped; private AsyncTask asyncTask; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); if (getSupportActionBar() != null) getSupportActionBar().setDisplayHomeAsUpEnabled(true); TextView create_an_account_peertube = findViewById(R.id.create_an_account_peertube); SpannableString content_create = new SpannableString(getString(R.string.join_peertube)); content_create.setSpan(new UnderlineSpan(), 0, content_create.length(), 0); content_create.setSpan(new ForegroundColorSpan(ContextCompat.getColor(LoginActivity.this, R.color.colorAccent)), 0, content_create.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE); create_an_account_peertube.setText(content_create); create_an_account_peertube.setOnClickListener(v -> { Intent mainActivity = new Intent(LoginActivity.this, PeertubeRegisterActivity.class); Bundle b = new Bundle(); mainActivity.putExtras(b); startActivity(mainActivity); }); login_uid = findViewById(R.id.login_uid); login_passwd = findViewById(R.id.login_passwd); connectionButton = findViewById(R.id.login_button); LinearLayout connected = findViewById(R.id.connected); LinearLayout not_connected = findViewById(R.id.not_connected); if (Helper.isLoggedIn(LoginActivity.this)) { not_connected.setVisibility(View.GONE); connected.setVisibility(View.VISIBLE); SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE); SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); String instance = sharedpreferences.getString(Helper.PREF_INSTANCE, Helper.getLiveInstance(LoginActivity.this)); TextView instanceView = findViewById(R.id.instance); Account account = new AccountDAO(LoginActivity.this, db).getUniqAccount(userId, instance); if (account == null) { account = new AccountDAO(LoginActivity.this, db).getUniqAccount(userId, Helper.getPeertubeUrl(instance)); } if (account != null) { ImageView profile_picture = findViewById(R.id.profile_picture); TextView username = findViewById(R.id.username); TextView displayname = findViewById(R.id.displayname); Helper.loadGiF(LoginActivity.this, account, profile_picture); username.setText(String.format("@%s", account.getUsername())); displayname.setText(account.getDisplay_name()); instanceView.setText(account.getInstance()); Button logout_button = findViewById(R.id.logout_button); Account finalAccount = account; logout_button.setOnClickListener(v -> { AlertDialog.Builder dialogBuilderLogoutAccount = new AlertDialog.Builder(LoginActivity.this); dialogBuilderLogoutAccount.setMessage(getString(R.string.logout_account_confirmation, finalAccount.getUsername())); dialogBuilderLogoutAccount.setPositiveButton(R.string.action_logout, (dialog, id) -> { Helper.logoutCurrentUser(LoginActivity.this, finalAccount); dialog.dismiss(); }); dialogBuilderLogoutAccount.setNegativeButton(R.string.cancel, (dialog, id) -> dialog.dismiss()); AlertDialog alertDialogLogoutAccount = dialogBuilderLogoutAccount.create(); alertDialogLogoutAccount.show(); }); } else { Helper.logoutCurrentUser(LoginActivity.this, null); } max_id = null; firstLoad = true; flag_loading = true; notifications = new ArrayList<>(); swiped = false; swipeRefreshLayout = findViewById(R.id.swipeContainer); RecyclerView lv_notifications = findViewById(R.id.lv_notifications); mainLoader = findViewById(R.id.loader); nextElementLoader = findViewById(R.id.loading_next_notifications); textviewNoAction = findViewById(R.id.no_action); mainLoader.setVisibility(View.VISIBLE); nextElementLoader.setVisibility(View.GONE); notificationsListAdapter = new PeertubeNotificationsListAdapter(this.notifications); lv_notifications.setAdapter(notificationsListAdapter); mLayoutManager = new LinearLayoutManager(LoginActivity.this); lv_notifications.setLayoutManager(mLayoutManager); lv_notifications.addOnScrollListener(new RecyclerView.OnScrollListener() { public void onScrolled(@NotNull RecyclerView recyclerView, int dx, int dy) { if (dy > 0) { int visibleItemCount = mLayoutManager.getChildCount(); int totalItemCount = mLayoutManager.getItemCount(); int firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition(); if (firstVisibleItem + visibleItemCount == totalItemCount) { if (!flag_loading) { flag_loading = true; asyncTask = new RetrievePeertubeNotificationsAsyncTask(LoginActivity.this, null, max_id, LoginActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); nextElementLoader.setVisibility(View.VISIBLE); } } else { nextElementLoader.setVisibility(View.GONE); } } } }); swipeRefreshLayout.setOnRefreshListener(() -> { max_id = null; firstLoad = true; flag_loading = true; swiped = true; asyncTask = new RetrievePeertubeNotificationsAsyncTask(LoginActivity.this, null, null, LoginActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }); asyncTask = new RetrievePeertubeNotificationsAsyncTask(LoginActivity.this, null, max_id, LoginActivity.this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } else { connected.setVisibility(View.GONE); not_connected.setVisibility(View.VISIBLE); } connectionButton.setOnClickListener(v -> { if (!android.util.Patterns.EMAIL_ADDRESS.matcher(login_uid.getText().toString().trim()).matches()) { Toasty.error(LoginActivity.this, getString(R.string.email_error)).show(); return; } String[] emailArray = login_uid.getText().toString().split("@"); if (emailArray.length > 1 && !Arrays.asList(Helper.valideEmails).contains(emailArray[1])) { Toasty.error(LoginActivity.this, getString(R.string.email_error_domain, emailArray[1])).show(); return; } String host = emailArray[1]; String instance = Helper.getPeertubeUrl(host); final HashMap parameters = new HashMap<>(); connectionButton.setEnabled(false); try { instance = URLEncoder.encode(instance, "utf-8"); } catch (UnsupportedEncodingException e) { Toasty.error(LoginActivity.this, getString(R.string.client_error), Toast.LENGTH_LONG).show(); } actionToken = "/api/v1/oauth-clients/local"; parameters.clear(); parameters.put(Helper.CLIENT_NAME, Helper.CLIENT_NAME_VALUE); parameters.put(Helper.REDIRECT_URIS, Helper.REDIRECT_CONTENT); parameters.put(Helper.SCOPES, Helper.OAUTH_SCOPES_PEERTUBE); parameters.put(Helper.WEBSITE, Helper.WEBSITE_VALUE); String finalInstance = instance; new Thread(() -> { try { String response = new HttpsConnection(LoginActivity.this).get("https://" + finalInstance + actionToken, 30, parameters, null); JSONObject resobj; try { resobj = new JSONObject(response); client_id = resobj.get(Helper.CLIENT_ID).toString(); client_secret = resobj.get(Helper.CLIENT_SECRET).toString(); SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE); SharedPreferences.Editor editor = sharedpreferences.edit(); editor.putString(Helper.CLIENT_ID, client_id); editor.putString(Helper.CLIENT_SECRET, client_secret); editor.apply(); parameters.clear(); parameters.put(Helper.CLIENT_ID, sharedpreferences.getString(Helper.CLIENT_ID, null)); parameters.put(Helper.CLIENT_SECRET, sharedpreferences.getString(Helper.CLIENT_SECRET, null)); parameters.put("grant_type", "password"); try { parameters.put("username", URLEncoder.encode(login_uid.getText().toString().trim().toLowerCase(), "UTF-8")); } catch (UnsupportedEncodingException e) { parameters.put("username", login_uid.getText().toString().trim().toLowerCase()); } try { parameters.put("password", URLEncoder.encode(login_passwd.getText().toString(), "UTF-8")); } catch (UnsupportedEncodingException e) { parameters.put("password", login_passwd.getText().toString()); } parameters.put("scope", "user"); String oauthUrl = "/api/v1/users/token"; try { String responseLogin = new HttpsConnection(LoginActivity.this).post("https://" + finalInstance + oauthUrl, 30, parameters, null); runOnUiThread(() -> { JSONObject resobjLogin; try { resobjLogin = new JSONObject(responseLogin); String token = resobjLogin.getString("access_token"); String refresh_token = resobjLogin.getString("refresh_token"); editor.putString(Helper.PREF_KEY_OAUTH_TOKEN, token); editor.putString(Helper.PREF_INSTANCE, host); editor.apply(); //Update the account with the token; new UpdateAccountInfoAsyncTask(LoginActivity.this, token, client_id, client_secret, refresh_token, host).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } catch (JSONException e) { e.printStackTrace(); runOnUiThread(() -> connectionButton.setEnabled(true)); } }); } catch (final Exception e) { e.printStackTrace(); runOnUiThread(() -> { connectionButton.setEnabled(true); String message; if (e.getLocalizedMessage() != null && e.getLocalizedMessage().trim().length() > 0) message = e.getLocalizedMessage(); else if (e.getMessage() != null && e.getMessage().trim().length() > 0) message = e.getMessage(); else message = getString(R.string.client_error); Toasty.error(LoginActivity.this, message, Toast.LENGTH_LONG).show(); }); } } catch (JSONException e) { e.printStackTrace(); e.printStackTrace(); runOnUiThread(() -> connectionButton.setEnabled(true)); } } catch (final Exception e) { e.printStackTrace(); runOnUiThread(() -> { connectionButton.setEnabled(true); String message = null; if (e.getLocalizedMessage() != null) { message = e.getMessage(); } if (message == null) { message = getString(R.string.client_error); } Toasty.error(LoginActivity.this, message, Toast.LENGTH_LONG).show(); }); } }).start(); }); } @Override protected void onResume() { super.onResume(); } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == android.R.id.home) { finish(); return true; } return super.onOptionsItemSelected(item); } @Override public void onDestroy() { super.onDestroy(); if (asyncTask != null && !asyncTask.isCancelled()) { asyncTask.cancel(true); } } @Override public void onRetrievePeertubeNotifications(APIResponse apiResponse, Account account) { mainLoader.setVisibility(View.GONE); nextElementLoader.setVisibility(View.GONE); if (apiResponse.getError() != null) { Toasty.error(LoginActivity.this, apiResponse.getError().getError(), Toast.LENGTH_LONG).show(); flag_loading = false; swipeRefreshLayout.setRefreshing(false); swiped = false; return; } int previousPosition = notifications.size(); max_id = apiResponse.getMax_id(); List notifications = apiResponse.getPeertubeNotifications(); if (!swiped && firstLoad && (notifications == null || notifications.size() == 0)) textviewNoAction.setVisibility(View.VISIBLE); else textviewNoAction.setVisibility(View.GONE); if (swiped) { if (previousPosition > 0) { this.notifications.subList(0, previousPosition).clear(); notificationsListAdapter.notifyItemRangeRemoved(0, previousPosition); } swiped = false; } if (notifications != null && notifications.size() > 0) { this.notifications.addAll(notifications); notificationsListAdapter.notifyItemRangeInserted(previousPosition, notifications.size()); } else { if (firstLoad) textviewNoAction.setVisibility(View.VISIBLE); } swipeRefreshLayout.setRefreshing(false); firstLoad = false; //The initial call comes from a classic tab refresh flag_loading = (max_id == null); } }