Auto-complete feature for the login page

This commit adds an auto-complete feature to select the users instance. It fetches all the instances from lemmyverse.net so everyone can find their instance :)

Closes #38
This commit is contained in:
Balazs Toldi 2023-11-09 20:17:24 +01:00
parent 9f02969dd2
commit 55f4689984
No known key found for this signature in database
GPG Key ID: 6C7D440036F99D58
10 changed files with 165 additions and 7 deletions

View File

@ -255,4 +255,13 @@ abstract class NetworkModule {
static LemmyPrivateMessageAPI provideLemmyPrivateMessageAPI(@Named("base") RetrofitHolder retrofit) {
return new LemmyPrivateMessageAPI(retrofit);
}
@Provides
@Named("lemmyVerse")
@Singleton
static Retrofit provideLemmyVerseRetrofit(@Named("base") RetrofitHolder retrofit) {
return retrofit.getRetrofit().newBuilder()
.baseUrl(APIUtils.LEMMYVERSE_API_BASE_URI)
.build();
}
}

View File

@ -10,12 +10,14 @@ import android.text.Editable;
import android.util.Log;
import android.view.InflateException;
import android.view.MenuItem;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.AppCompatAutoCompleteTextView;
import androidx.appcompat.widget.Toolbar;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
@ -28,6 +30,8 @@ import org.json.JSONObject;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import javax.inject.Inject;
@ -40,11 +44,15 @@ import eu.toldi.infinityforlemmy.Infinity;
import eu.toldi.infinityforlemmy.R;
import eu.toldi.infinityforlemmy.RedditDataRoomDatabase;
import eu.toldi.infinityforlemmy.RetrofitHolder;
import eu.toldi.infinityforlemmy.adapters.CustomArrayAdapter;
import eu.toldi.infinityforlemmy.apis.LemmyAPI;
import eu.toldi.infinityforlemmy.asynctasks.ParseAndInsertNewAccount;
import eu.toldi.infinityforlemmy.customtheme.CustomThemeWrapper;
import eu.toldi.infinityforlemmy.customviews.slidr.Slidr;
import eu.toldi.infinityforlemmy.dto.AccountLoginDTO;
import eu.toldi.infinityforlemmy.lemmyverse.FetchInstancesListener;
import eu.toldi.infinityforlemmy.lemmyverse.LemmyInstance;
import eu.toldi.infinityforlemmy.lemmyverse.LemmyVerseFetchInstances;
import eu.toldi.infinityforlemmy.site.FetchSiteInfo;
import eu.toldi.infinityforlemmy.site.SiteInfo;
import eu.toldi.infinityforlemmy.utils.SharedPreferencesUtils;
@ -69,7 +77,7 @@ public class LoginActivity extends BaseActivity {
TextView twoFAInfoTextView;
@BindView(R.id.instance_url_input)
TextInputEditText instance_input;
AppCompatAutoCompleteTextView instance_input;
@BindView(R.id.username_input)
TextInputEditText username_input;
@BindView(R.id.user_password_input)
@ -86,9 +94,11 @@ public class LoginActivity extends BaseActivity {
@Inject
@Named("no_oauth")
RetrofitHolder mRetrofit;
@Inject
@Named("oauth")
Retrofit mOauthRetrofit;
@Named("lemmyVerse")
Retrofit mLemmyVerseRetrofit;
@Inject
RedditDataRoomDatabase mRedditDataRoomDatabase;
@Inject
@ -139,9 +149,23 @@ public class LoginActivity extends BaseActivity {
isAgreeToUserAgreement = savedInstanceState.getBoolean(IS_AGREE_TO_USER_AGGREMENT_STATE);
}
LemmyVerseFetchInstances.INSTANCE.fetchInstances(mLemmyVerseRetrofit, new FetchInstancesListener() {
@Override
public void onFetchInstancesSuccess(@NonNull List<LemmyInstance> instances) {
ArrayList<String> instanceNames = new ArrayList<>();
for (LemmyInstance instance : instances) {
instanceNames.add(instance.getFqdn());
}
ArrayAdapter<String> adapter = new CustomArrayAdapter(LoginActivity.this, android.R.layout.simple_dropdown_item_1line, instanceNames, mCustomThemeWrapper);
instance_input.setAdapter(adapter);
}
});
loginButton.setOnClickListener(view -> {
Log.i("LoginActivity", "Login button clicked");
if(!checkFields())
if (!checkFields())
return;
loginButton.setEnabled(false);
progressBar.setVisibility(ProgressBar.VISIBLE);
@ -338,6 +362,7 @@ public class LoginActivity extends BaseActivity {
if (typeface != null) {
twoFAInfoTextView.setTypeface(typeface);
}
instance_input.setTextColor(mCustomThemeWrapper.getPrimaryTextColor());
}
@Override

View File

@ -0,0 +1,41 @@
package eu.toldi.infinityforlemmy.adapters;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import androidx.annotation.NonNull;
import java.util.List;
import eu.toldi.infinityforlemmy.customtheme.CustomThemeWrapper;
public class CustomArrayAdapter extends ArrayAdapter<String> {
CustomThemeWrapper customThemeWrapper;
public CustomArrayAdapter(@NonNull Context context, int textViewResourceId, @NonNull List<String> objects, CustomThemeWrapper customThemeWrapper) {
super(context, textViewResourceId, objects);
this.customThemeWrapper = customThemeWrapper;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView itemView = (TextView) super.getView(position, convertView, parent);
itemView.setTextColor(customThemeWrapper.getPrimaryTextColor()); // Set the text color
itemView.setBackgroundColor(customThemeWrapper.getBackgroundColor()); // Set the background color
// Apply any other styling as needed
return itemView;
}
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
TextView itemView = (TextView) super.getDropDownView(position, convertView, parent);
itemView.setTextColor(customThemeWrapper.getPrimaryTextColor()); // Set the text color
itemView.setBackgroundColor(customThemeWrapper.getBackgroundColor()); // Set the background color
// Apply any other styling as needed
return itemView;
}
}

View File

@ -115,6 +115,7 @@ public class APIUtils {
public static final String REVEDDIT_ORIGIN = "https://www.reveddit.com";
public static final String REFERER_KEY = "Referer";
public static final String REVEDDIT_REFERER = "https://www.reveddit.com/";
public static final String LEMMYVERSE_API_BASE_URI = "https://data.lemmyverse.net";
public static Map<String, String> getHttpBasicAuthHeader() {
Map<String, String> params = new HashMap<>();

View File

@ -0,0 +1,13 @@
package eu.toldi.infinityforlemmy.apis
import retrofit2.Call
import retrofit2.http.GET
import retrofit2.http.Headers
interface LemmyVerseAPI {
@Headers("Content-Type: application/json")
@GET("/data/instance.min.json")
fun getInstanceList(): Call<String?>?
}

View File

@ -0,0 +1,3 @@
package eu.toldi.infinityforlemmy.lemmyverse
data class LemmyInstance(val name: String, val fqdn: String)

View File

@ -0,0 +1,39 @@
package eu.toldi.infinityforlemmy.lemmyverse
import eu.toldi.infinityforlemmy.apis.LemmyVerseAPI
import retrofit2.Retrofit
object LemmyVerseFetchInstances {
fun fetchInstances(
lemmyVerseRetrofit: Retrofit,
fetchInstancesListener: FetchInstancesListener
) {
val lemmyVerseAPI = lemmyVerseRetrofit.create(LemmyVerseAPI::class.java)
val call = lemmyVerseAPI.getInstanceList()
call?.enqueue(object : retrofit2.Callback<String?> {
override fun onResponse(
call: retrofit2.Call<String?>,
response: retrofit2.Response<String?>
) {
if (response.isSuccessful) {
val instances = LemmyVerseParseInstances.parseInstances(response.body())
fetchInstancesListener.onFetchInstancesSuccess(instances)
} else {
fetchInstancesListener.onFetchInstancesSuccess(listOf())
}
}
override fun onFailure(call: retrofit2.Call<String?>, t: Throwable) {
fetchInstancesListener.onFetchInstancesSuccess(listOf())
}
})
}
}
fun interface FetchInstancesListener {
fun onFetchInstancesSuccess(instances: List<LemmyInstance>)
}

View File

@ -0,0 +1,24 @@
package eu.toldi.infinityforlemmy.lemmyverse
import org.json.JSONArray
object LemmyVerseParseInstances {
fun parseInstances(body: String?): List<LemmyInstance> {
val instances: MutableList<LemmyInstance> = ArrayList()
try {
val jsonBody = JSONArray(body)
for (i in 0 until jsonBody.length()) {
val jsonInstance = jsonBody.getJSONObject(i)
val name = jsonInstance.getString("name")
val url = jsonInstance.getString("base")
instances.add(LemmyInstance(name, url))
}
} catch (e: Exception) {
e.printStackTrace()
}
return instances
}
}

View File

@ -50,13 +50,15 @@
android:layout_height="wrap_content"
android:hint="@string/instance_url">
<com.google.android.material.textfield.TextInputEditText
<androidx.appcompat.widget.AppCompatAutoCompleteTextView
android:id="@+id/instance_url_input"
style="@style/Widget.MaterialComponents.AutoCompleteTextView.FilledBox"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:hint="@string/instance_url"
android:inputType="textUri"
android:maxLines="1" />
android:maxLines="1"
android:paddingTop="16dp" />
</com.google.android.material.textfield.TextInputLayout>
@ -66,7 +68,7 @@
android:layout_height="wrap_content"
android:layout_marginLeft="3dp"
android:layout_marginRight="3dp"
android:text="The URL of you prefered Lemmy instance with or without the https:// prefix" />
android:text="@string/the_url_of_you_prefered_lemmy_instance_with_or_without_the_https_prefix" />
<com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent"

View File

@ -1454,4 +1454,5 @@
<string name="settings_credits_new_icon_summary">Made by David Gerla</string>
<string name="original_app">The original Reddit app</string>
<string name="settings_credits_original_app_description">"Infinity was created by u/Hostilenemy "</string>
<string name="the_url_of_you_prefered_lemmy_instance_with_or_without_the_https_prefix">The URL of you prefered Lemmy instance with or without the https:// prefix</string>
</resources>