Merge branch 'main' into fork
This commit is contained in:
@@ -75,7 +75,7 @@ dependencies {
|
|||||||
implementation 'me.grishka.litex:dynamicanimation:1.1.0-alpha03'
|
implementation 'me.grishka.litex:dynamicanimation:1.1.0-alpha03'
|
||||||
implementation 'me.grishka.litex:viewpager:1.0.0'
|
implementation 'me.grishka.litex:viewpager:1.0.0'
|
||||||
implementation 'me.grishka.litex:viewpager2:1.0.0'
|
implementation 'me.grishka.litex:viewpager2:1.0.0'
|
||||||
implementation 'me.grishka.appkit:appkit:1.2.6'
|
implementation 'me.grishka.appkit:appkit:1.2.7'
|
||||||
implementation 'com.google.code.gson:gson:2.8.9'
|
implementation 'com.google.code.gson:gson:2.8.9'
|
||||||
implementation 'org.jsoup:jsoup:1.14.3'
|
implementation 'org.jsoup:jsoup:1.14.3'
|
||||||
implementation 'com.squareup:otto:1.3.8'
|
implementation 'com.squareup:otto:1.3.8'
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import me.grishka.appkit.Nav;
|
import me.grishka.appkit.Nav;
|
||||||
|
import me.grishka.appkit.api.Callback;
|
||||||
|
import me.grishka.appkit.api.ErrorResponse;
|
||||||
import me.grishka.appkit.api.SimpleCallback;
|
import me.grishka.appkit.api.SimpleCallback;
|
||||||
import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
@@ -128,7 +130,7 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
|||||||
}else{
|
}else{
|
||||||
progressVisibilityListener.onProgressVisibilityChanged(true);
|
progressVisibilityListener.onProgressVisibilityChanged(true);
|
||||||
currentRequest=new GetSearchResults(currentQuery, null, true)
|
currentRequest=new GetSearchResults(currentQuery, null, true)
|
||||||
.setCallback(new SimpleCallback<>(this){
|
.setCallback(new Callback<>(){
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(SearchResults result){
|
public void onSuccess(SearchResults result){
|
||||||
ArrayList<SearchResult> results=new ArrayList<>();
|
ArrayList<SearchResult> results=new ArrayList<>();
|
||||||
@@ -148,6 +150,17 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
|||||||
unfilteredResults=results;
|
unfilteredResults=results;
|
||||||
onDataLoaded(filterSearchResults(results), false);
|
onDataLoaded(filterSearchResults(results), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ErrorResponse error){
|
||||||
|
currentRequest=null;
|
||||||
|
Activity a=getActivity();
|
||||||
|
if(a==null)
|
||||||
|
return;
|
||||||
|
error.showToast(a);
|
||||||
|
if(progressVisibilityListener!=null)
|
||||||
|
progressVisibilityListener.onProgressVisibilityChanged(false);
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.exec(accountID);
|
.exec(accountID);
|
||||||
}
|
}
|
||||||
@@ -159,12 +172,11 @@ public class SearchFragment extends BaseStatusListFragment<SearchResult>{
|
|||||||
super.updateList();
|
super.updateList();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
imgLoader.deactivate();
|
|
||||||
UiUtils.updateList(prevDisplayItems, displayItems, list, adapter, (i1, i2)->i1.parentID.equals(i2.parentID) && i1.index==i2.index && i1.getType()==i2.getType());
|
UiUtils.updateList(prevDisplayItems, displayItems, list, adapter, (i1, i2)->i1.parentID.equals(i2.parentID) && i1.index==i2.index && i1.getType()==i2.getType());
|
||||||
boolean recent=isInRecentMode();
|
boolean recent=isInRecentMode();
|
||||||
if(recent!=headerAdapter.isVisible())
|
if(recent!=headerAdapter.isVisible())
|
||||||
headerAdapter.setVisible(recent);
|
headerAdapter.setVisible(recent);
|
||||||
imgLoader.activate();
|
imgLoader.forceUpdateImages();
|
||||||
prevDisplayItems=null;
|
prevDisplayItems=null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.joinmastodon.android.fragments.onboarding;
|
package org.joinmastodon.android.fragments.onboarding;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@@ -9,6 +10,7 @@ import android.os.LocaleList;
|
|||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
|
import android.util.Log;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -21,6 +23,7 @@ import android.widget.RadioButton;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.joinmastodon.android.R;
|
import org.joinmastodon.android.R;
|
||||||
|
import org.joinmastodon.android.api.MastodonAPIController;
|
||||||
import org.joinmastodon.android.api.MastodonAPIRequest;
|
import org.joinmastodon.android.api.MastodonAPIRequest;
|
||||||
import org.joinmastodon.android.api.MastodonErrorResponse;
|
import org.joinmastodon.android.api.MastodonErrorResponse;
|
||||||
import org.joinmastodon.android.api.requests.instance.GetInstance;
|
import org.joinmastodon.android.api.requests.instance.GetInstance;
|
||||||
@@ -36,7 +39,13 @@ import org.joinmastodon.android.ui.M3AlertDialogBuilder;
|
|||||||
import org.joinmastodon.android.ui.tabs.TabLayout;
|
import org.joinmastodon.android.ui.tabs.TabLayout;
|
||||||
import org.joinmastodon.android.ui.utils.UiUtils;
|
import org.joinmastodon.android.ui.utils.UiUtils;
|
||||||
import org.parceler.Parcels;
|
import org.parceler.Parcels;
|
||||||
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
|
import org.w3c.dom.Node;
|
||||||
|
import org.w3c.dom.NodeList;
|
||||||
|
import org.xml.sax.InputSource;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.net.IDN;
|
import java.net.IDN;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -46,6 +55,8 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import javax.xml.parsers.DocumentBuilderFactory;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.recyclerview.widget.DiffUtil;
|
import androidx.recyclerview.widget.DiffUtil;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
@@ -58,6 +69,9 @@ import me.grishka.appkit.utils.MergeRecyclerAdapter;
|
|||||||
import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
|
import me.grishka.appkit.utils.SingleViewRecyclerAdapter;
|
||||||
import me.grishka.appkit.utils.V;
|
import me.grishka.appkit.utils.V;
|
||||||
import me.grishka.appkit.views.UsableRecyclerView;
|
import me.grishka.appkit.views.UsableRecyclerView;
|
||||||
|
import okhttp3.Call;
|
||||||
|
import okhttp3.Request;
|
||||||
|
import okhttp3.Response;
|
||||||
|
|
||||||
public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstance>{
|
public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstance>{
|
||||||
private InstancesAdapter adapter;
|
private InstancesAdapter adapter;
|
||||||
@@ -75,9 +89,11 @@ public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstanc
|
|||||||
private List<CatalogCategory> categories=new ArrayList<>();
|
private List<CatalogCategory> categories=new ArrayList<>();
|
||||||
private String loadingInstanceDomain;
|
private String loadingInstanceDomain;
|
||||||
private GetInstance loadingInstanceRequest;
|
private GetInstance loadingInstanceRequest;
|
||||||
|
private Call loadingInstanceRedirectRequest;
|
||||||
private HashMap<String, Instance> instancesCache=new HashMap<>();
|
private HashMap<String, Instance> instancesCache=new HashMap<>();
|
||||||
private ProgressDialog instanceProgressDialog;
|
private ProgressDialog instanceProgressDialog;
|
||||||
private View buttonBar;
|
private View buttonBar;
|
||||||
|
private HashMap<String, String> redirects=new HashMap<>(), redirectsInverse=new HashMap<>();
|
||||||
|
|
||||||
private boolean isSignup;
|
private boolean isSignup;
|
||||||
|
|
||||||
@@ -271,7 +287,7 @@ public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstanc
|
|||||||
}else{
|
}else{
|
||||||
showProgressDialog();
|
showProgressDialog();
|
||||||
if(!domain.equals(loadingInstanceDomain)){
|
if(!domain.equals(loadingInstanceDomain)){
|
||||||
loadInstanceInfo(domain);
|
loadInstanceInfo(domain, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -353,7 +369,7 @@ public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstanc
|
|||||||
Instance instance=instancesCache.get(normalizeInstanceDomain(currentSearchQuery));
|
Instance instance=instancesCache.get(normalizeInstanceDomain(currentSearchQuery));
|
||||||
if(instance==null){
|
if(instance==null){
|
||||||
showProgressDialog();
|
showProgressDialog();
|
||||||
loadInstanceInfo(currentSearchQuery);
|
loadInstanceInfo(currentSearchQuery, false);
|
||||||
}else{
|
}else{
|
||||||
proceedWithAuthOrSignup(instance);
|
proceedWithAuthOrSignup(instance);
|
||||||
}
|
}
|
||||||
@@ -363,7 +379,7 @@ public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstanc
|
|||||||
private void onSearchChangedDebounced(){
|
private void onSearchChangedDebounced(){
|
||||||
currentSearchQuery=searchEdit.getText().toString().toLowerCase();
|
currentSearchQuery=searchEdit.getText().toString().toLowerCase();
|
||||||
updateFilteredList();
|
updateFilteredList();
|
||||||
loadInstanceInfo(currentSearchQuery);
|
loadInstanceInfo(currentSearchQuery, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateFilteredList(){
|
private void updateFilteredList(){
|
||||||
@@ -403,13 +419,7 @@ public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstanc
|
|||||||
private void showProgressDialog(){
|
private void showProgressDialog(){
|
||||||
instanceProgressDialog=new ProgressDialog(getActivity());
|
instanceProgressDialog=new ProgressDialog(getActivity());
|
||||||
instanceProgressDialog.setMessage(getString(R.string.loading_instance));
|
instanceProgressDialog.setMessage(getString(R.string.loading_instance));
|
||||||
instanceProgressDialog.setOnCancelListener(dialog->{
|
instanceProgressDialog.setOnCancelListener(dialog->cancelLoadingInstanceInfo());
|
||||||
if(loadingInstanceRequest!=null){
|
|
||||||
loadingInstanceRequest.cancel();
|
|
||||||
loadingInstanceRequest=null;
|
|
||||||
loadingInstanceDomain=null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
instanceProgressDialog.show();
|
instanceProgressDialog.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -429,10 +439,12 @@ public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstanc
|
|||||||
}catch(IllegalArgumentException x){
|
}catch(IllegalArgumentException x){
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
if(redirects.containsKey(domain))
|
||||||
|
return redirects.get(domain);
|
||||||
return domain;
|
return domain;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadInstanceInfo(String _domain){
|
private void loadInstanceInfo(String _domain, boolean isFromRedirect){
|
||||||
String domain=normalizeInstanceDomain(_domain);
|
String domain=normalizeInstanceDomain(_domain);
|
||||||
Instance cachedInstance=instancesCache.get(domain);
|
Instance cachedInstance=instancesCache.get(domain);
|
||||||
if(cachedInstance!=null){
|
if(cachedInstance!=null){
|
||||||
@@ -446,10 +458,11 @@ public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstanc
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(loadingInstanceDomain!=null){
|
if(loadingInstanceDomain!=null){
|
||||||
if(loadingInstanceDomain.equals(domain))
|
if(loadingInstanceDomain.equals(domain)){
|
||||||
return;
|
return;
|
||||||
else
|
}else{
|
||||||
loadingInstanceRequest.cancel();
|
cancelLoadingInstanceInfo();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
loadingInstanceDomain=domain;
|
loadingInstanceDomain=domain;
|
||||||
loadingInstanceRequest=new GetInstance();
|
loadingInstanceRequest=new GetInstance();
|
||||||
@@ -465,7 +478,7 @@ public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstanc
|
|||||||
instanceProgressDialog=null;
|
instanceProgressDialog=null;
|
||||||
proceedWithAuthOrSignup(result);
|
proceedWithAuthOrSignup(result);
|
||||||
}
|
}
|
||||||
if(domain.equals(currentSearchQuery)){
|
if(domain.equals(currentSearchQuery) || currentSearchQuery.equals(redirects.get(domain)) || currentSearchQuery.equals(redirectsInverse.get(domain))){
|
||||||
boolean found=false;
|
boolean found=false;
|
||||||
for(CatalogInstance ci:filteredData){
|
for(CatalogInstance ci:filteredData){
|
||||||
if(ci.domain.equals(domain)){
|
if(ci.domain.equals(domain)){
|
||||||
@@ -484,20 +497,105 @@ public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstanc
|
|||||||
@Override
|
@Override
|
||||||
public void onError(ErrorResponse error){
|
public void onError(ErrorResponse error){
|
||||||
loadingInstanceRequest=null;
|
loadingInstanceRequest=null;
|
||||||
loadingInstanceDomain=null;
|
if(!isFromRedirect && error instanceof MastodonErrorResponse me && me.httpStatus==404){
|
||||||
if(instanceProgressDialog!=null){
|
fetchDomainFromHostMetaAndMaybeRetry(domain, error);
|
||||||
instanceProgressDialog.dismiss();
|
return;
|
||||||
instanceProgressDialog=null;
|
|
||||||
new M3AlertDialogBuilder(getActivity())
|
|
||||||
.setTitle(R.string.error)
|
|
||||||
.setMessage(getString(R.string.not_a_mastodon_instance, domain)+"\n\n"+((MastodonErrorResponse)error).error)
|
|
||||||
.setPositiveButton(R.string.ok, null)
|
|
||||||
.show();
|
|
||||||
}
|
}
|
||||||
|
loadingInstanceDomain=null;
|
||||||
|
showInstanceInfoLoadError(domain, error);
|
||||||
}
|
}
|
||||||
}).execNoAuth(domain);
|
}).execNoAuth(domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void cancelLoadingInstanceInfo(){
|
||||||
|
if(loadingInstanceRequest!=null){
|
||||||
|
loadingInstanceRequest.cancel();
|
||||||
|
loadingInstanceRequest=null;
|
||||||
|
}
|
||||||
|
if(loadingInstanceRedirectRequest!=null){
|
||||||
|
loadingInstanceRedirectRequest.cancel();
|
||||||
|
loadingInstanceRedirectRequest=null;
|
||||||
|
}
|
||||||
|
loadingInstanceDomain=null;
|
||||||
|
if(instanceProgressDialog!=null){
|
||||||
|
instanceProgressDialog.dismiss();
|
||||||
|
instanceProgressDialog=null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showInstanceInfoLoadError(String domain, Object error){
|
||||||
|
if(instanceProgressDialog!=null){
|
||||||
|
instanceProgressDialog.dismiss();
|
||||||
|
instanceProgressDialog=null;
|
||||||
|
String additionalInfo;
|
||||||
|
if(error instanceof MastodonErrorResponse me){
|
||||||
|
additionalInfo="\n\n"+me.error;
|
||||||
|
}else if(error instanceof Throwable t){
|
||||||
|
additionalInfo="\n\n"+t.getLocalizedMessage();
|
||||||
|
}else{
|
||||||
|
additionalInfo="";
|
||||||
|
}
|
||||||
|
new M3AlertDialogBuilder(getActivity())
|
||||||
|
.setTitle(R.string.error)
|
||||||
|
.setMessage(getString(R.string.not_a_mastodon_instance, domain)+additionalInfo)
|
||||||
|
.setPositiveButton(R.string.ok, null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fetchDomainFromHostMetaAndMaybeRetry(String domain, Object origError){
|
||||||
|
String url="https://"+domain+"/.well-known/host-meta";
|
||||||
|
Request req=new Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.build();
|
||||||
|
loadingInstanceRedirectRequest=MastodonAPIController.getHttpClient().newCall(req);
|
||||||
|
loadingInstanceRedirectRequest.enqueue(new okhttp3.Callback(){
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call call, @NonNull IOException e){
|
||||||
|
loadingInstanceRedirectRequest=null;
|
||||||
|
loadingInstanceDomain=null;
|
||||||
|
Activity a=getActivity();
|
||||||
|
if(a==null)
|
||||||
|
return;
|
||||||
|
a.runOnUiThread(()->showInstanceInfoLoadError(domain, e));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException{
|
||||||
|
loadingInstanceRedirectRequest=null;
|
||||||
|
loadingInstanceDomain=null;
|
||||||
|
Activity a=getActivity();
|
||||||
|
if(a==null)
|
||||||
|
return;
|
||||||
|
try(response){
|
||||||
|
if(!response.isSuccessful()){
|
||||||
|
a.runOnUiThread(()->showInstanceInfoLoadError(domain, response.code()+" "+response.message()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
InputSource source=new InputSource(response.body().charStream());
|
||||||
|
Document doc=DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(source);
|
||||||
|
NodeList list=doc.getElementsByTagName("Link");
|
||||||
|
for(int i=0;i<list.getLength();i++){
|
||||||
|
if(list.item(i) instanceof Element el){
|
||||||
|
String template=el.getAttribute("template");
|
||||||
|
if("lrdd".equals(el.getAttribute("rel")) && !TextUtils.isEmpty(template) && template.contains("{uri}")){
|
||||||
|
Uri uri=Uri.parse(template.replace("{uri}", "qwe"));
|
||||||
|
String redirectDomain=normalizeInstanceDomain(uri.getHost());
|
||||||
|
redirects.put(domain, redirectDomain);
|
||||||
|
redirectsInverse.put(redirectDomain, domain);
|
||||||
|
a.runOnUiThread(()->loadInstanceInfo(redirectDomain, true));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
a.runOnUiThread(()->showInstanceInfoLoadError(domain, origError));
|
||||||
|
}catch(Exception x){
|
||||||
|
a.runOnUiThread(()->showInstanceInfoLoadError(domain, x));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onApplyWindowInsets(WindowInsets insets){
|
public void onApplyWindowInsets(WindowInsets insets){
|
||||||
if(Build.VERSION.SDK_INT>=27){
|
if(Build.VERSION.SDK_INT>=27){
|
||||||
@@ -580,7 +678,7 @@ public class InstanceCatalogFragment extends BaseRecyclerFragment<CatalogInstanc
|
|||||||
if(chosenInstance==null)
|
if(chosenInstance==null)
|
||||||
nextButton.setEnabled(true);
|
nextButton.setEnabled(true);
|
||||||
chosenInstance=item;
|
chosenInstance=item;
|
||||||
loadInstanceInfo(chosenInstance.domain);
|
loadInstanceInfo(chosenInstance.domain, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ public abstract class ImageStatusDisplayItem extends StatusDisplayItem{
|
|||||||
@Override
|
@Override
|
||||||
public void clearImage(int index){
|
public void clearImage(int index){
|
||||||
crossfadeDrawable.setCrossfadeAlpha(1f);
|
crossfadeDrawable.setCrossfadeAlpha(1f);
|
||||||
|
crossfadeDrawable.setImageDrawable(null);
|
||||||
didClear=true;
|
didClear=true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:layout_width="120dp"
|
android:layout_width="120dp"
|
||||||
android:layout_height="72dp"
|
android:layout_height="72dp"
|
||||||
android:paddingTop="13dp"
|
android:paddingTop="12dp"
|
||||||
android:paddingBottom="13dp">
|
android:paddingBottom="12dp">
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/emoji"
|
android:id="@+id/emoji"
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
<TextView
|
<TextView
|
||||||
android:id="@android:id/text1"
|
android:id="@android:id/text1"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="16dp"
|
android:layout_height="18dp"
|
||||||
android:layout_gravity="center_horizontal"
|
android:layout_gravity="center_horizontal"
|
||||||
android:textAllCaps="true"
|
android:textAllCaps="true"
|
||||||
android:textColor="?android:textColorPrimary"
|
android:textColor="?android:textColorPrimary"
|
||||||
|
|||||||
Reference in New Issue
Block a user