353 lines
17 KiB
Java
353 lines
17 KiB
Java
package app.fedilab.android.activities;
|
|
/* Copyright 2022 Thomas Schneider
|
|
*
|
|
* This file is a part of Fedilab
|
|
*
|
|
* 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.
|
|
*
|
|
* Fedilab 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 Fedilab; if not,
|
|
* see <http://www.gnu.org/licenses>. */
|
|
|
|
|
|
import static app.fedilab.android.helper.PinnedTimelineHelper.sortPositionAsc;
|
|
|
|
import android.content.Intent;
|
|
import android.graphics.Paint;
|
|
import android.os.Bundle;
|
|
import android.os.Handler;
|
|
import android.text.Editable;
|
|
import android.text.InputFilter;
|
|
import android.text.TextWatcher;
|
|
import android.view.Menu;
|
|
import android.view.MenuItem;
|
|
import android.view.View;
|
|
import android.view.WindowManager;
|
|
import android.view.inputmethod.InputMethodManager;
|
|
import android.widget.ArrayAdapter;
|
|
import android.widget.Toast;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.appcompat.app.AlertDialog;
|
|
import androidx.lifecycle.ViewModelProvider;
|
|
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
|
import androidx.recyclerview.widget.ItemTouchHelper;
|
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
|
import androidx.recyclerview.widget.RecyclerView;
|
|
|
|
import java.io.IOException;
|
|
import java.util.ArrayList;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
import app.fedilab.android.R;
|
|
import app.fedilab.android.client.entities.InstanceSocial;
|
|
import app.fedilab.android.client.entities.Pinned;
|
|
import app.fedilab.android.client.entities.Timeline;
|
|
import app.fedilab.android.client.entities.app.PinnedTimeline;
|
|
import app.fedilab.android.client.entities.app.RemoteInstance;
|
|
import app.fedilab.android.databinding.ActivityReorderTabsBinding;
|
|
import app.fedilab.android.databinding.PopupSearchInstanceBinding;
|
|
import app.fedilab.android.exception.DBException;
|
|
import app.fedilab.android.helper.Helper;
|
|
import app.fedilab.android.helper.ThemeHelper;
|
|
import app.fedilab.android.helper.itemtouchhelper.OnStartDragListener;
|
|
import app.fedilab.android.helper.itemtouchhelper.OnUndoListener;
|
|
import app.fedilab.android.helper.itemtouchhelper.SimpleItemTouchHelperCallback;
|
|
import app.fedilab.android.ui.drawer.ReorderTabAdapter;
|
|
import app.fedilab.android.viewmodel.mastodon.InstanceSocialVM;
|
|
import app.fedilab.android.viewmodel.mastodon.ReorderVM;
|
|
import es.dmoral.toasty.Toasty;
|
|
import okhttp3.Call;
|
|
import okhttp3.Callback;
|
|
import okhttp3.OkHttpClient;
|
|
import okhttp3.Request;
|
|
import okhttp3.Response;
|
|
|
|
|
|
public class ReorderTimelinesActivity extends BaseActivity implements OnStartDragListener, OnUndoListener {
|
|
|
|
|
|
private ItemTouchHelper touchHelper;
|
|
private ReorderTabAdapter adapter;
|
|
private boolean searchInstanceRunning;
|
|
private String oldSearch;
|
|
private Pinned pinned;
|
|
private ActivityReorderTabsBinding binding;
|
|
private boolean changes;
|
|
|
|
@Override
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
super.onCreate(savedInstanceState);
|
|
ThemeHelper.applyThemeBar(this);
|
|
binding = ActivityReorderTabsBinding.inflate(getLayoutInflater());
|
|
setContentView(binding.getRoot());
|
|
searchInstanceRunning = false;
|
|
if (getSupportActionBar() != null)
|
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
|
|
|
changes = false;
|
|
ReorderVM reorderVM = new ViewModelProvider(ReorderTimelinesActivity.this).get(ReorderVM.class);
|
|
reorderVM.getPinned().observe(ReorderTimelinesActivity.this, _pinned -> {
|
|
this.pinned = _pinned;
|
|
if (this.pinned == null) {
|
|
this.pinned = new Pinned();
|
|
this.pinned.pinnedTimelines = new ArrayList<>();
|
|
}
|
|
sortPositionAsc(this.pinned.pinnedTimelines);
|
|
adapter = new ReorderTabAdapter(this.pinned, ReorderTimelinesActivity.this, ReorderTimelinesActivity.this);
|
|
ItemTouchHelper.Callback callback =
|
|
new SimpleItemTouchHelperCallback(adapter);
|
|
touchHelper = new ItemTouchHelper(callback);
|
|
touchHelper.attachToRecyclerView(binding.lvReorderTabs);
|
|
binding.lvReorderTabs.setAdapter(adapter);
|
|
LinearLayoutManager mLayoutManager = new LinearLayoutManager(ReorderTimelinesActivity.this);
|
|
binding.lvReorderTabs.setLayoutManager(mLayoutManager);
|
|
});
|
|
|
|
}
|
|
|
|
@Override
|
|
public boolean onOptionsItemSelected(MenuItem item) {
|
|
if (item.getItemId() == android.R.id.home) {
|
|
finish();
|
|
return true;
|
|
} else if (item.getItemId() == R.id.action_add_timeline) {
|
|
addInstance();
|
|
}
|
|
|
|
return super.onOptionsItemSelected(item);
|
|
}
|
|
|
|
|
|
@Override
|
|
public boolean onCreateOptionsMenu(@NonNull Menu menu) {
|
|
getMenuInflater().inflate(R.menu.menu_reorder, menu);
|
|
return super.onCreateOptionsMenu(menu);
|
|
}
|
|
|
|
private void addInstance() {
|
|
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(ReorderTimelinesActivity.this, Helper.dialogStyle());
|
|
PopupSearchInstanceBinding popupSearchInstanceBinding = PopupSearchInstanceBinding.inflate(getLayoutInflater());
|
|
dialogBuilder.setView(popupSearchInstanceBinding.getRoot());
|
|
popupSearchInstanceBinding.setAttachmentGroup.setOnCheckedChangeListener((group, checkedId) -> {
|
|
if (checkedId == R.id.twitter_accounts) {
|
|
popupSearchInstanceBinding.searchInstance.setHint(R.string.list_of_twitter_accounts);
|
|
} else {
|
|
popupSearchInstanceBinding.searchInstance.setHint(R.string.instance);
|
|
}
|
|
});
|
|
popupSearchInstanceBinding.searchInstance.setFilters(new InputFilter[]{new InputFilter.LengthFilter(60)});
|
|
dialogBuilder.setPositiveButton(R.string.validate, (dialog, id) -> {
|
|
String instanceName = popupSearchInstanceBinding.searchInstance.getText().toString().trim().replace("@", "");
|
|
new Thread(() -> {
|
|
String url = null;
|
|
if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.mastodon_instance)
|
|
url = "https://" + instanceName + "/api/v1/timelines/public?local=true";
|
|
else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.peertube_instance)
|
|
url = "https://" + instanceName + "/api/v1/videos/";
|
|
else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.pixelfed_instance) {
|
|
url = "https://" + instanceName + "/api/v1/timelines/public";
|
|
} else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.misskey_instance) {
|
|
url = "https://" + instanceName + "/api/notes/local-timeline";
|
|
} else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.gnu_instance) {
|
|
url = "https://" + instanceName + "/api/statuses/public_timeline.json";
|
|
}
|
|
OkHttpClient client = new OkHttpClient.Builder()
|
|
.connectTimeout(10, TimeUnit.SECONDS)
|
|
.writeTimeout(10, TimeUnit.SECONDS)
|
|
.proxy(Helper.getProxy(getApplication().getApplicationContext()))
|
|
.readTimeout(10, TimeUnit.SECONDS).build();
|
|
Request request;
|
|
if (url != null) {
|
|
request = new Request.Builder()
|
|
.url(url)
|
|
.build();
|
|
client.newCall(request).enqueue(new Callback() {
|
|
@Override
|
|
public void onFailure(@NonNull Call call, @NonNull IOException e) {
|
|
e.printStackTrace();
|
|
runOnUiThread(() -> Toasty.warning(ReorderTimelinesActivity.this, getString(R.string.toast_instance_unavailable), Toast.LENGTH_LONG).show());
|
|
}
|
|
|
|
@Override
|
|
public void onResponse(@NonNull Call call, @NonNull final Response response) {
|
|
if (!response.isSuccessful()) {
|
|
runOnUiThread(() -> {
|
|
dialog.dismiss();
|
|
RemoteInstance.InstanceType instanceType = null;
|
|
if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.mastodon_instance) {
|
|
instanceType = RemoteInstance.InstanceType.MASTODON;
|
|
} else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.peertube_instance) {
|
|
instanceType = RemoteInstance.InstanceType.PEERTUBE;
|
|
} else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.pixelfed_instance) {
|
|
instanceType = RemoteInstance.InstanceType.PIXELFED;
|
|
} else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.misskey_instance) {
|
|
instanceType = RemoteInstance.InstanceType.MISSKEY;
|
|
} else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.gnu_instance) {
|
|
instanceType = RemoteInstance.InstanceType.GNU;
|
|
} else if (popupSearchInstanceBinding.setAttachmentGroup.getCheckedRadioButtonId() == R.id.twitter_accounts) {
|
|
instanceType = RemoteInstance.InstanceType.NITTER;
|
|
}
|
|
RemoteInstance remoteInstance = new RemoteInstance();
|
|
remoteInstance.type = instanceType;
|
|
remoteInstance.host = instanceName;
|
|
PinnedTimeline pinnedTimeline = new PinnedTimeline();
|
|
pinnedTimeline.remoteInstance = remoteInstance;
|
|
pinnedTimeline.displayed = true;
|
|
pinnedTimeline.type = Timeline.TimeLineEnum.REMOTE;
|
|
pinnedTimeline.position = pinned.pinnedTimelines.size();
|
|
pinned.pinnedTimelines.add(pinnedTimeline);
|
|
try {
|
|
new Pinned(ReorderTimelinesActivity.this).updatePinned(pinned);
|
|
changes = true;
|
|
adapter.notifyItemInserted(pinned.pinnedTimelines.size());
|
|
} catch (DBException e) {
|
|
e.printStackTrace();
|
|
}
|
|
});
|
|
} else {
|
|
runOnUiThread(() -> Toasty.warning(ReorderTimelinesActivity.this, getString(R.string.toast_instance_unavailable), Toast.LENGTH_LONG).show());
|
|
}
|
|
}
|
|
});
|
|
} else {
|
|
runOnUiThread(() -> Toasty.warning(ReorderTimelinesActivity.this, getString(R.string.toast_instance_unavailable), Toast.LENGTH_LONG).show());
|
|
}
|
|
}).start();
|
|
});
|
|
AlertDialog alertDialog = dialogBuilder.create();
|
|
alertDialog.setOnDismissListener(dialogInterface -> {
|
|
//Hide keyboard
|
|
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
|
|
assert imm != null;
|
|
imm.hideSoftInputFromWindow(popupSearchInstanceBinding.searchInstance.getWindowToken(), 0);
|
|
});
|
|
if (alertDialog.getWindow() != null)
|
|
alertDialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
|
|
alertDialog.show();
|
|
|
|
popupSearchInstanceBinding.searchInstance.setOnItemClickListener((parent, view1, position, id) -> oldSearch = parent.getItemAtPosition(position).toString().trim());
|
|
|
|
popupSearchInstanceBinding.searchInstance.addTextChangedListener(new TextWatcher() {
|
|
@Override
|
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
|
}
|
|
|
|
@Override
|
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
|
|
|
}
|
|
|
|
@Override
|
|
public void afterTextChanged(Editable s) {
|
|
|
|
if (s.length() > 2 && !searchInstanceRunning) {
|
|
String query = s.toString().trim();
|
|
if (query.startsWith("http://")) {
|
|
query = query.replace("http://", "");
|
|
}
|
|
if (query.startsWith("https://")) {
|
|
query = query.replace("https://", "");
|
|
}
|
|
if (oldSearch == null || !oldSearch.equals(s.toString().trim())) {
|
|
searchInstanceRunning = true;
|
|
InstanceSocialVM instanceSocialVM = new ViewModelProvider(ReorderTimelinesActivity.this).get(InstanceSocialVM.class);
|
|
instanceSocialVM.getInstances(query).observe(ReorderTimelinesActivity.this, instanceSocialList -> {
|
|
popupSearchInstanceBinding.searchInstance.setAdapter(null);
|
|
String[] instances = new String[instanceSocialList.instances.size()];
|
|
int j = 0;
|
|
for (InstanceSocial.Instance instance : instanceSocialList.instances) {
|
|
instances[j] = instance.name;
|
|
j++;
|
|
}
|
|
ArrayAdapter<String> arrayAdapter =
|
|
new ArrayAdapter<>(ReorderTimelinesActivity.this, android.R.layout.simple_list_item_1, instances);
|
|
popupSearchInstanceBinding.searchInstance.setAdapter(arrayAdapter);
|
|
if (popupSearchInstanceBinding.searchInstance.hasFocus() && !isFinishing())
|
|
popupSearchInstanceBinding.searchInstance.showDropDown();
|
|
if (oldSearch != null && oldSearch.equals(popupSearchInstanceBinding.searchInstance.getText().toString())) {
|
|
popupSearchInstanceBinding.searchInstance.dismissDropDown();
|
|
}
|
|
|
|
oldSearch = s.toString().trim();
|
|
searchInstanceRunning = false;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
}
|
|
|
|
@Override
|
|
protected void onPause() {
|
|
super.onPause();
|
|
if (changes) {
|
|
//Update menu
|
|
Bundle b = new Bundle();
|
|
b.putBoolean(Helper.RECEIVE_REDRAW_TOPBAR, true);
|
|
Intent intentBD = new Intent(Helper.BROADCAST_DATA);
|
|
intentBD.putExtras(b);
|
|
LocalBroadcastManager.getInstance(ReorderTimelinesActivity.this).sendBroadcast(intentBD);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onStartDrag(RecyclerView.ViewHolder viewHolder) {
|
|
touchHelper.startDrag(viewHolder);
|
|
}
|
|
|
|
|
|
@Override
|
|
public void onUndo(PinnedTimeline pinnedTimeline, int position) {
|
|
binding.undoContainer.setVisibility(View.VISIBLE);
|
|
switch (pinnedTimeline.type) {
|
|
case TAG:
|
|
binding.undoMessage.setText(R.string.reorder_tag_removed);
|
|
break;
|
|
case REMOTE:
|
|
binding.undoMessage.setText(R.string.reorder_instance_removed);
|
|
break;
|
|
case LIST:
|
|
binding.undoMessage.setText(R.string.reorder_list_deleted);
|
|
break;
|
|
}
|
|
binding.undoAction.setPaintFlags(binding.undoAction.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
|
|
Runnable runnable = () -> {
|
|
binding.undoContainer.setVisibility(View.GONE);
|
|
//change position of pinned that are after the removed item
|
|
for (int i = pinnedTimeline.position + 1; i < pinned.pinnedTimelines.size(); i++) {
|
|
pinned.pinnedTimelines.get(i).position -= 1;
|
|
}
|
|
pinned.pinnedTimelines.remove(pinnedTimeline);
|
|
adapter.notifyItemRemoved(position);
|
|
try {
|
|
new Pinned(ReorderTimelinesActivity.this).updatePinned(pinned);
|
|
changes = true;
|
|
} catch (DBException e) {
|
|
e.printStackTrace();
|
|
}
|
|
};
|
|
Handler handler = new Handler();
|
|
handler.postDelayed(runnable, 4000);
|
|
binding.undoAction.setOnClickListener(v -> {
|
|
pinned.pinnedTimelines.add(position, pinnedTimeline);
|
|
adapter.notifyItemInserted(position);
|
|
binding.undoContainer.setVisibility(View.GONE);
|
|
handler.removeCallbacks(runnable);
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void onStop() {
|
|
super.onStop();
|
|
}
|
|
|
|
|
|
}
|