Make the default server configurable

This commit is contained in:
Grishka 2023-07-02 16:22:32 +03:00
parent 85d4c1fc24
commit d8dfa6017d
7 changed files with 150 additions and 14 deletions

View File

@ -9,8 +9,8 @@ android {
applicationId "org.joinmastodon.android"
minSdk 23
targetSdk 33
versionCode 60
versionName "2.0.0"
versionCode 61
versionName "2.0.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
resConfigs "ar-rSA", "be-rBY", "bn-rBD", "bs-rBA", "ca-rES", "cs-rCZ", "da-rDK", "de-rDE", "el-rGR", "es-rES", "eu-rES", "fa-rIR", "fi-rFI", "fil-rPH", "fr-rFR", "ga-rIE", "gd-rGB", "gl-rES", "hi-rIN", "hr-rHR", "hu-rHU", "hy-rAM", "ig-rNG", "in-rID", "is-rIS", "it-rIT", "iw-rIL", "ja-rJP", "kab", "ko-rKR", "my-rMM", "nl-rNL", "no-rNO", "oc-rFR", "pl-rPL", "pt-rBR", "pt-rPT", "ro-rRO", "ru-rRU", "si-rLK", "sl-rSI", "sv-rSE", "th-rTH", "tr-rTR", "uk-rUA", "ur-rIN", "vi-rVN", "zh-rCN", "zh-rTW"
}

View File

@ -24,6 +24,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -86,6 +87,9 @@ public class MastodonAPIController{
synchronized(req){
req.okhttpCall=call;
}
if(req.timeout>0){
call.timeout().timeout(req.timeout, TimeUnit.MILLISECONDS);
}
if(BuildConfig.DEBUG)
Log.d(TAG, "["+(session==null ? "no-auth" : session.getID())+"] Sending request: "+hreq);

View File

@ -45,6 +45,7 @@ public abstract class MastodonAPIRequest<T> extends APIRequest<T>{
Token token;
boolean canceled;
Map<String, String> headers;
long timeout;
private ProgressDialog progressDialog;
protected boolean removeUnsupportedItems;
@ -127,6 +128,10 @@ public abstract class MastodonAPIRequest<T> extends APIRequest<T>{
headers.put(key, value);
}
protected void setTimeout(long timeout){
this.timeout=timeout;
}
protected String getPathPrefix(){
return "/api/v1";
}

View File

@ -0,0 +1,22 @@
package org.joinmastodon.android.api.requests.catalog;
import android.net.Uri;
import com.google.gson.reflect.TypeToken;
import org.joinmastodon.android.api.MastodonAPIRequest;
import org.joinmastodon.android.model.catalog.CatalogDefaultInstance;
import java.util.List;
public class GetCatalogDefaultInstances extends MastodonAPIRequest<List<CatalogDefaultInstance>>{
public GetCatalogDefaultInstances(){
super(HttpMethod.GET, null, new TypeToken<>(){});
setTimeout(500);
}
@Override
public Uri getURL(){
return Uri.parse("https://api.joinmastodon.org/default-servers");
}
}

View File

@ -7,20 +7,27 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.widget.Button;
import android.widget.ProgressBar;
import org.joinmastodon.android.MastodonApp;
import org.joinmastodon.android.R;
import org.joinmastodon.android.api.requests.catalog.GetCatalogDefaultInstances;
import org.joinmastodon.android.api.requests.instance.GetInstance;
import org.joinmastodon.android.fragments.onboarding.InstanceCatalogSignupFragment;
import org.joinmastodon.android.fragments.onboarding.InstanceChooserLoginFragment;
import org.joinmastodon.android.fragments.onboarding.InstanceRulesFragment;
import org.joinmastodon.android.model.Instance;
import org.joinmastodon.android.model.catalog.CatalogDefaultInstance;
import org.joinmastodon.android.ui.InterpolatingMotionEffect;
import org.joinmastodon.android.ui.M3AlertDialogBuilder;
import org.joinmastodon.android.ui.utils.UiUtils;
import org.joinmastodon.android.ui.views.ProgressBarButton;
import org.joinmastodon.android.ui.views.SizeListenerFrameLayout;
import org.parceler.Parcels;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import androidx.annotation.Nullable;
import me.grishka.appkit.Nav;
import me.grishka.appkit.api.Callback;
@ -37,11 +44,16 @@ public class SplashFragment extends AppKitFragment{
private View artContainer, blueFill, greenFill;
private InterpolatingMotionEffect motionEffect;
private View artClouds, artPlaneElephant, artRightHill, artLeftHill, artCenterHill;
private ProgressBarButton defaultServerButton;
private ProgressBar defaultServerProgress;
private String chosenDefaultServer=DEFAULT_SERVER;
private boolean loadingDefaultServer;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
motionEffect=new InterpolatingMotionEffect(MastodonApp.context);
loadAndChooseDefaultServer();
}
@Nullable
@ -50,9 +62,14 @@ public class SplashFragment extends AppKitFragment{
contentView=(SizeListenerFrameLayout) inflater.inflate(R.layout.fragment_splash, container, false);
contentView.findViewById(R.id.btn_get_started).setOnClickListener(this::onButtonClick);
contentView.findViewById(R.id.btn_log_in).setOnClickListener(this::onButtonClick);
Button joinDefault=contentView.findViewById(R.id.btn_join_default_server);
joinDefault.setText(getString(R.string.join_default_server, DEFAULT_SERVER));
joinDefault.setOnClickListener(this::onJoinDefaultServerClick);
defaultServerButton=contentView.findViewById(R.id.btn_join_default_server);
defaultServerButton.setText(getString(R.string.join_default_server, chosenDefaultServer));
defaultServerButton.setOnClickListener(this::onJoinDefaultServerClick);
defaultServerProgress=contentView.findViewById(R.id.action_progress);
if(loadingDefaultServer){
defaultServerButton.setTextVisible(false);
defaultServerProgress.setVisibility(View.VISIBLE);
}
contentView.findViewById(R.id.btn_learn_more).setOnClickListener(this::onLearnMoreClick);
artClouds=contentView.findViewById(R.id.art_clouds);
@ -96,12 +113,22 @@ public class SplashFragment extends AppKitFragment{
}
private void onJoinDefaultServerClick(View v){
if(loadingDefaultServer)
return;
new GetInstance()
.setCallback(new Callback<>(){
@Override
public void onSuccess(Instance result){
if(getActivity()==null)
return;
if(!result.registrations){
new M3AlertDialogBuilder(getActivity())
.setTitle(R.string.error)
.setMessage(R.string.instance_signup_closed)
.setPositiveButton(R.string.ok, null)
.show();
return;
}
Bundle args=new Bundle();
args.putParcelable("instance", Parcels.wrap(result));
Nav.go(getActivity(), InstanceRulesFragment.class, args);
@ -115,7 +142,7 @@ public class SplashFragment extends AppKitFragment{
}
})
.wrapProgress(getActivity(), R.string.loading_instance, true)
.execNoAuth(DEFAULT_SERVER);
.execNoAuth(chosenDefaultServer);
}
private void onLearnMoreClick(View v){
@ -168,4 +195,54 @@ public class SplashFragment extends AppKitFragment{
super.onHidden();
motionEffect.deactivate();
}
private void loadAndChooseDefaultServer(){
loadingDefaultServer=true;
new GetCatalogDefaultInstances()
.setCallback(new Callback<>(){
@Override
public void onSuccess(List<CatalogDefaultInstance> result){
if(result.isEmpty()){
setChosenDefaultServer(DEFAULT_SERVER);
return;
}
float sum=0f;
for(CatalogDefaultInstance inst:result){
sum+=inst.weight;
}
if(sum<=0)
sum=1f;
for(CatalogDefaultInstance inst:result){
inst.weight/=sum;
}
float rand=ThreadLocalRandom.current().nextFloat();
float prev=0f;
for(CatalogDefaultInstance inst:result){
if(rand>=prev && rand<prev+inst.weight){
setChosenDefaultServer(inst.domain);
return;
}
prev+=inst.weight;
}
// Just in case something didn't add up
setChosenDefaultServer(result.get(result.size()-1).domain);
}
@Override
public void onError(ErrorResponse error){
setChosenDefaultServer(DEFAULT_SERVER);
}
})
.execNoAuth("");
}
private void setChosenDefaultServer(String domain){
chosenDefaultServer=domain;
loadingDefaultServer=false;
if(defaultServerButton!=null && getActivity()!=null){
defaultServerButton.setTextVisible(true);
defaultServerProgress.setVisibility(View.GONE);
defaultServerButton.setText(getString(R.string.join_default_server, chosenDefaultServer));
}
}
}

View File

@ -0,0 +1,10 @@
package org.joinmastodon.android.model.catalog;
import org.joinmastodon.android.api.AllFieldsAreRequired;
import org.joinmastodon.android.model.BaseModel;
@AllFieldsAreRequired
public class CatalogDefaultInstance extends BaseModel{
public String domain;
public float weight;
}

View File

@ -104,14 +104,32 @@
android:layout_height="0px"
android:layout_weight="1"/>
<Button
android:id="@+id/btn_join_default_server"
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
style="@style/Widget.Mastodon.M3.Button.Filled"
tools:text="@string/join_default_server"/>
android:layout_height="wrap_content">
<org.joinmastodon.android.ui.views.ProgressBarButton
android:id="@+id/btn_join_default_server"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
style="@style/Widget.Mastodon.M3.Button.Filled"
tools:text="@string/join_default_server"/>
<ProgressBar
android:id="@+id/action_progress"
style="?android:progressBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:elevation="10dp"
android:indeterminate="true"
android:outlineProvider="none"
android:indeterminateTint="#FFF"
android:visibility="gone" />
</FrameLayout>
<Button
android:id="@+id/btn_get_started"