refactored ServerListAdapter and converted it to kotlin

This commit is contained in:
kosharskiy 2021-01-14 09:55:22 +02:00
parent 4f12fd94ff
commit 2a1d2058e3
10 changed files with 274 additions and 291 deletions

View File

@ -16,12 +16,16 @@
*/
package net.schueller.peertube.activity
import android.app.Activity
import android.app.AlertDialog
import android.content.DialogInterface
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.viewModels
import androidx.fragment.app.FragmentManager
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import net.schueller.peertube.R
@ -30,6 +34,9 @@ import net.schueller.peertube.database.Server
import net.schueller.peertube.database.ServerViewModel
import net.schueller.peertube.databinding.ActivityServerAddressBookBinding
import net.schueller.peertube.fragment.AddServerFragment
import net.schueller.peertube.helper.APIUrlHelper
import net.schueller.peertube.network.Session
import net.schueller.peertube.service.LoginService
import java.util.*
class ServerAddressBookActivity : CommonActivity() {
@ -77,8 +84,32 @@ class ServerAddressBookActivity : CommonActivity() {
}
}
private fun onServerClick(server: Server) {
val sharedPref = PreferenceManager.getDefaultSharedPreferences(this)
val editor = sharedPref.edit()
val serverUrl = APIUrlHelper.cleanServerUrl(server.serverHost)
editor.putString(getString(R.string.pref_api_base_key), serverUrl)
editor.apply()
// Logout if logged in
val session = Session.getInstance()
if (session.isLoggedIn) {
session.invalidate()
}
// attempt authentication if we have a username
if (server.username.isNullOrBlank().not()) {
LoginService.Authenticate(server.username, server.password)
}
// close this activity
finish()
Toast.makeText(this, getString(R.string.server_selection_set_server, serverUrl), Toast.LENGTH_LONG).show()
}
private fun showServers() {
val adapter = ServerListAdapter(this).also {
val adapter = ServerListAdapter(mutableListOf()) { onServerClick(it) }.also {
mBinding.serverListRecyclerview.adapter = it
}
@ -110,7 +141,7 @@ class ServerAddressBookActivity : CommonActivity() {
// Update the cached copy of the words in the adapter.
mServerViewModel.allServers.observe(this, { servers: List<Server?>? ->
mServerViewModel.allServers.observe(this, { servers: List<Server> ->
adapter.setServers(servers)
addServerFragment?.let {

View File

@ -1,156 +0,0 @@
/*
* Copyright (C) 2020 Stefan Schüller <sschueller@techdroid.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.schueller.peertube.adapter;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import net.schueller.peertube.R;
import net.schueller.peertube.database.Server;
import net.schueller.peertube.helper.APIUrlHelper;
import net.schueller.peertube.network.Session;
import net.schueller.peertube.service.LoginService;
import java.util.List;
import static android.app.Activity.RESULT_OK;
public class ServerListAdapter extends RecyclerView.Adapter<ServerListAdapter.ServerViewHolder> {
private final LayoutInflater mInflater;
private List<Server> mServers; // Cached copy of Servers
public ServerListAdapter(Context context) {
this.mInflater = LayoutInflater.from(context);
}
@NonNull
@Override
public ServerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View itemView = mInflater.inflate(R.layout.row_server_address_book, parent, false);
return new ServerViewHolder(itemView);
}
@Override
public void onBindViewHolder(@NonNull ServerViewHolder holder, int position) {
if (mServers != null) {
Server current = mServers.get(position);
holder.serverLabel.setText(current.getServerName());
holder.serverUrl.setText(current.getServerHost());
if (TextUtils.isEmpty(current.getUsername())) {
holder.hasLogin.setVisibility(View.GONE);
} else {
holder.hasLogin.setVisibility(View.VISIBLE);
}
} else {
// Covers the case of data not being ready yet.
holder.serverLabel.setText(R.string.server_book_no_servers_found);
}
holder.itemView.setOnClickListener(v -> {
SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(mInflater.getContext());
SharedPreferences.Editor editor = sharedPref.edit();
String serverUrl = APIUrlHelper.cleanServerUrl(getServerAtPosition(position).getServerHost());
editor.putString(mInflater.getContext().getString(R.string.pref_api_base_key), serverUrl);
editor.apply();
// Logout if logged in
Session session = Session.getInstance();
if (session.isLoggedIn()) {
session.invalidate();
}
// attempt authentication if we have a username
if (!TextUtils.isEmpty(getServerAtPosition(position).getUsername())) {
LoginService.Authenticate(
getServerAtPosition(position).getUsername(),
getServerAtPosition(position).getPassword()
);
}
// tell server list activity to reload list
Intent intent = new Intent();
((Activity) mInflater.getContext()).setResult(RESULT_OK, intent);
// close this activity
((Activity) mInflater.getContext()).finish();
Toast.makeText(mInflater.getContext(), mInflater.getContext().getString(R.string.server_selection_set_server, serverUrl), Toast.LENGTH_LONG).show();
});
//
// holder.itemView.setOnLongClickListener(v -> {
// Log.v("ServerListAdapter", "setOnLongClickListener " + position);
// return true;
// });
}
public void setServers(List<Server> Servers) {
mServers = Servers;
this.notifyDataSetChanged();
}
// getItemCount() is called many times, and when it is first called,
// mServers has not been updated (means initially, it's null, and we can't return null).
@Override
public int getItemCount() {
if (mServers != null)
return mServers.size();
else return 0;
}
static class ServerViewHolder extends RecyclerView.ViewHolder {
TextView serverLabel, serverUrl, serverUsername;
ImageView hasLogin;
private ServerViewHolder(View itemView) {
super(itemView);
serverLabel = itemView.findViewById(R.id.serverLabelRow);
serverUrl = itemView.findViewById(R.id.serverUrlRow);
hasLogin = itemView.findViewById(R.id.sb_row_has_login_icon);
}
}
public Server getServerAtPosition (int position) {
return mServers.get(position);
}
}

View File

@ -0,0 +1,75 @@
/*
* Copyright (C) 2020 Stefan Schüller <sschueller@techdroid.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.schueller.peertube.adapter
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import net.schueller.peertube.adapter.ServerListAdapter.ServerViewHolder
import net.schueller.peertube.database.Server
import net.schueller.peertube.databinding.RowServerAddressBookBinding
import net.schueller.peertube.utils.visibleIf
class ServerListAdapter(private val mServers: MutableList<Server>, private val onClick: (Server) -> Unit) : RecyclerView.Adapter<ServerViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ServerViewHolder {
val binding = RowServerAddressBookBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ServerViewHolder(binding)
}
override fun onBindViewHolder(holder: ServerViewHolder, position: Int) {
holder.bind(mServers[position])
//
// holder.itemView.setOnLongClickListener(v -> {
// Log.v("ServerListAdapter", "setOnLongClickListener " + position);
// return true;
// });
}
fun setServers(servers: List<Server>) {
mServers.clear()
mServers.addAll(servers)
notifyDataSetChanged()
}
// getItemCount() is called many times, and when it is first called,
// mServers has not been updated (means initially, it's null, and we can't return null).
override fun getItemCount(): Int {
return mServers.size
}
inner class ServerViewHolder (private val binding: RowServerAddressBookBinding) : RecyclerView.ViewHolder(binding.root) {
fun bind(server: Server) {
binding.serverLabelRow.text = server.serverName
binding.serverUrlRow.text = server.serverHost
binding.sbRowHasLoginIcon.visibleIf { server.username.isNullOrBlank().not() }
binding.root.setOnClickListener { onClick(server) }
}
}
fun getServerAtPosition(position: Int): Server {
return mServers[position]
}
}

View File

@ -1,86 +0,0 @@
/*
* Copyright (C) 2020 Stefan Schüller <sschueller@techdroid.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.schueller.peertube.database;
import androidx.annotation.NonNull;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
@Entity(tableName = "server_table")
public class Server {
@PrimaryKey(autoGenerate = true)
private int id;
@NonNull
@ColumnInfo(name = "server_name")
private String serverName;
@ColumnInfo(name = "server_host")
private String serverHost;
@ColumnInfo(name = "username")
private String username;
@ColumnInfo(name = "password")
private String password;
public Server(@NonNull String serverName) {
this.serverName = serverName;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getServerName() {
return serverName;
}
public void setServerName(String serverName) {
this.serverName = serverName;
}
public String getServerHost() {
return serverHost;
}
public void setServerHost(String serverHost) {
this.serverHost = serverHost;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright (C) 2020 Stefan Schüller <sschueller@techdroid.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.schueller.peertube.database
import android.os.Parcelable
import androidx.room.PrimaryKey
import androidx.room.ColumnInfo
import androidx.room.Entity
import kotlinx.android.parcel.Parcelize
@Parcelize
@Entity(tableName = "server_table")
data class Server(
@PrimaryKey(autoGenerate = true)
var id: Int = 0,
@ColumnInfo(name = "server_name")
var serverName: String,
@ColumnInfo(name = "server_host")
var serverHost: String? = null,
@ColumnInfo(name = "username")
var username: String? = null,
@ColumnInfo(name = "password")
var password: String? = null
) : Parcelable

View File

@ -42,10 +42,17 @@ class AddServerFragment : Fragment() {
private val mServerViewModel: ServerViewModel by activityViewModels()
private var mServer: Server? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
mServer = it.getParcelable(SERVER_ARG)
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
Log.d(TAG, "onCreateView")
// Inflate the layout for this fragment
mBinding = FragmentAddServerBinding.inflate(inflater, container, false)
return mBinding.root
}
@ -53,22 +60,20 @@ class AddServerFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// bind button click
initServerEdit()
mBinding.addServerButton.setOnClickListener { view: View? ->
mBinding.addServerButton.setOnClickListener {
var formValid = true
hideKeyboard()
if (mBinding.serverLabel.text.toString().isNullOrBlank()) {
if (mBinding.serverLabel.text.toString().isBlank()) {
mBinding.serverLabel.error = getString(R.string.server_book_label_is_required)
Toast.makeText(context, R.string.invalid_url, Toast.LENGTH_LONG).show()
formValid = false
}
// validate url
mBinding.serverUrl.apply {
APIUrlHelper.cleanServerUrl(text.toString())?.let {
@ -85,7 +90,7 @@ class AddServerFragment : Fragment() {
if (formValid) {
mBinding.apply {
val server = Server(serverLabel.text.toString())
val server = Server(serverName = serverLabel.text.toString())
server.serverHost = serverUrl.text.toString()
server.username = serverUsername.text.toString()
@ -96,21 +101,26 @@ class AddServerFragment : Fragment() {
}
}
// Button testServerButton = mView.findViewById(R.id.testServerButton);
// testServerButton.setOnClickListener(view -> {
// Activity act = getActivity();
// if (act instanceof ServerAddressBookActivity) {
// ((ServerAddressBookActivity) act).testServer();
// }
// });
mBinding.pickServerUrl.setOnClickListener {
val intentServer = Intent(activity, SearchServerActivity::class.java)
this.startActivityForResult(intentServer, PICK_SERVER)
}
}
private fun initServerEdit() {
mServer?.let {
mBinding.apply {
serverLabel.setText(it.serverName)
serverUrl.setText(it.serverHost)
serverUsername.setText(it.username)
serverPassword.setText(it.password)
addServerButton.text = getString(R.string.server_book_add_save_button)
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
@ -136,7 +146,16 @@ class AddServerFragment : Fragment() {
}
companion object {
const val TAG = "AddServerFragment"
const val PICK_SERVER = 1
private const val TAG = "AddServerFragment"
private const val PICK_SERVER = 1
private const val SERVER_ARG = "server"
fun newInstance(server: Server) = AddServerFragment().apply {
arguments = Bundle().also {
it.putParcelable(SERVER_ARG, server)
}
}
}
}

View File

@ -1,10 +1,44 @@
package net.schueller.peertube.utils
import android.content.Context
import android.view.View
import android.view.inputmethod.InputMethodManager
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
fun View.gone() {
this.visibility = View.GONE
}
fun View.visible() {
this.visibility = View.VISIBLE
}
fun View.invisible() {
this.visibility = View.INVISIBLE
}
fun View.visibleIf(predicate: () -> Boolean) {
when (predicate.invoke()) {
true -> visible()
else -> gone()
}
}
fun View.goneIf(predicate: () -> Boolean) {
when (predicate.invoke()) {
true -> gone()
else -> visible()
}
}
fun View.invisibleIf(predicate: () -> Boolean) {
when (predicate.invoke()) {
true -> invisible()
else -> visible()
}
}
fun Fragment.hideKeyboard(): Boolean {
activity?.currentFocus?.let {
val imm = context?.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
</vector>

View File

@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
@ -9,43 +11,52 @@
card_view:cardElevation="0dp"
card_view:cardUseCompatPadding="true">
<RelativeLayout
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:padding="12dp">
<LinearLayout
android:layout_alignParentStart="true"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView
android:id="@+id/serverLabelRow"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="@style/Base.TextAppearance.AppCompat.Headline" />
card_view:layout_constraintTop_toTopOf="parent"
card_view:layout_constraintStart_toStartOf="parent"
android:textAppearance="@style/Base.TextAppearance.AppCompat.Headline"
tools:text="@tools:sample/lorem"
/>
<TextView
android:id="@+id/serverUrlRow"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="0dp"
android:textAppearance="@style/Base.TextAppearance.AppCompat.Subhead" />
</LinearLayout>
card_view:layout_constraintTop_toBottomOf="@id/serverLabelRow"
card_view:layout_constraintStart_toStartOf="parent"
android:textAppearance="@style/Base.TextAppearance.AppCompat.Subhead"
tools:text="@tools:sample/lorem"
/>
<ImageView
android:id="@+id/sb_row_has_login_icon"
android:src="@drawable/ic_baseline_account_circle_24"
android:visibility="visible"
android:contentDescription="@string/server_book_list_has_login"
android:layout_width="24dp"
android:layout_alignParentEnd="true"
android:layout_height="wrap_content"/>
android:layout_height="wrap_content"
card_view:layout_constraintTop_toTopOf="parent"
card_view:layout_constraintEnd_toEndOf="parent"
android:contentDescription="@string/server_book_list_has_login"
android:src="@drawable/ic_baseline_account_circle_24"
/>
</RelativeLayout>
<ImageView
android:id="@+id/edit_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
card_view:layout_constraintTop_toBottomOf="@id/sb_row_has_login_icon"
card_view:layout_constraintEnd_toEndOf="parent"
card_view:layout_constraintBottom_toBottomOf="parent"
android:contentDescription="@string/server_book_list_has_login"
android:src="@drawable/ic_edit_24"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>

View File

@ -335,6 +335,7 @@
<string name="server_book_add_username">Username</string>
<string name="server_book_add_password">Password</string>
<string name="server_book_add_add_button">Add</string>
<string name="server_book_add_save_button">Save</string>
<string name="server_book_list_has_login">Has Login</string>
<string name="login_current_server_hint">Current Server</string>
<string name="title_activity_server_address_book">Address Book</string>