diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 71ab6b438..c44239e66 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -470,6 +470,10 @@ android:name="app.fedilab.android.activities.InstanceHealthActivity" android:excludeFromRecents="true" android:theme="@style/Base.V7.Theme.AppCompat.Dialog" /> + . */ +package app.fedilab.android.activities; + + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Build; +import android.os.Bundle; +import android.text.Html; +import android.text.SpannableString; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import com.bumptech.glide.Glide; + +import java.util.ArrayList; +import java.util.List; + +import app.fedilab.android.R; +import app.fedilab.android.asynctasks.RetrieveAccountsAsyncTask; +import app.fedilab.android.client.API; +import app.fedilab.android.client.Entities.Account; +import app.fedilab.android.client.Entities.InstanceNodeInfo; +import app.fedilab.android.drawers.AccountsListAdapter; +import app.fedilab.android.helper.Helper; + +import static androidx.core.text.HtmlCompat.FROM_HTML_MODE_LEGACY; + + +/** + * Created by Thomas on 15/11/2019. + * Instance info activity class + */ + +public class InstanceProfileActivity extends BaseActivity { + + + private TextView name, description, userCount, statusCount, instanceCount, software, version; + private String instance; + private RecyclerView lv_accounts; + private LinearLayout instance_container; + private ImageView back_ground_image; + private RelativeLayout loader; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE); + int theme = sharedpreferences.getInt(Helper.SET_THEME, Helper.THEME_DARK); + if (theme == Helper.THEME_LIGHT) { + setTheme(R.style.Dialog); + } else { + setTheme(R.style.DialogDark); + } + setContentView(R.layout.activity_instance_profile); + getWindow().setLayout(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + Bundle b = getIntent().getExtras(); + if (getSupportActionBar() != null) + getSupportActionBar().hide(); + if (b != null) + instance = b.getString("instance",null); + if( instance == null){ + finish(); + } + Button close = findViewById(R.id.close); + name = findViewById(R.id.name); + description = findViewById(R.id.description); + userCount = findViewById(R.id.user_count); + statusCount = findViewById(R.id.status_count); + instanceCount = findViewById(R.id.instance_count); + instance_container = findViewById(R.id.instance_container); + loader = findViewById(R.id.loader); + back_ground_image = findViewById(R.id.back_ground_image); + software = findViewById(R.id.software); + version = findViewById(R.id.version); + lv_accounts = findViewById(R.id.lv_accounts); + close.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + finish(); + } + }); + checkInstance(); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + finish(); + return true; + default: + return super.onOptionsItemSelected(item); + } + } + + + private void checkInstance() { + + if (instance == null) + return; + new Thread(new Runnable() { + @Override + public void run() { + try { + InstanceNodeInfo instanceNodeInfo = new API(InstanceProfileActivity.this).instanceInfo(instance.trim()); + runOnUiThread(new Runnable() { + public void run() { + if( instanceNodeInfo == null){ + finish(); + return; + } + if (instanceNodeInfo.getThumbnail() != null && !instanceNodeInfo.getThumbnail().equals("null")) + Glide.with(getApplicationContext()) + .asBitmap() + .load(instanceNodeInfo.getThumbnail()) + .into(back_ground_image); + name.setText(instanceNodeInfo.getNodeName()); + + + SpannableString descriptionSpan; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) + descriptionSpan = new SpannableString(Html.fromHtml(instanceNodeInfo.getNodeDescription(), FROM_HTML_MODE_LEGACY)); + else + descriptionSpan = new SpannableString(Html.fromHtml(instanceNodeInfo.getNodeDescription())); + description.setText(descriptionSpan, TextView.BufferType.SPANNABLE); + userCount.setText(Helper.withSuffix((instanceNodeInfo.getNumberOfUsers()))); + statusCount.setText(Helper.withSuffix((instanceNodeInfo.getNumberOfPosts()))); + instanceCount.setText(Helper.withSuffix((instanceNodeInfo.getNumberOfInstance()))); + software.setText(instanceNodeInfo.getName() + " - "); + version.setText(instanceNodeInfo.getVersion()); + if( instanceNodeInfo.getStaffAccount() != null){ + List accounts = new ArrayList<>(); + accounts.add(instanceNodeInfo.getStaffAccount()); + final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); + AccountsListAdapter accountsListAdapter = new AccountsListAdapter(RetrieveAccountsAsyncTask.Type.FOLLOWERS, userId, accounts); + lv_accounts.setAdapter(accountsListAdapter); + final LinearLayoutManager mLayoutManager; + mLayoutManager = new LinearLayoutManager(InstanceProfileActivity.this); + lv_accounts.setLayoutManager(mLayoutManager); + } + instance_container.setVisibility(View.VISIBLE); + loader.setVisibility(View.GONE); + } + }); + + } catch (Exception ignored) { + } + } + }).start(); + } + + +} diff --git a/app/src/main/java/app/fedilab/android/activities/ShowAccountActivity.java b/app/src/main/java/app/fedilab/android/activities/ShowAccountActivity.java index c6dab6d53..c1588c5bf 100644 --- a/app/src/main/java/app/fedilab/android/activities/ShowAccountActivity.java +++ b/app/src/main/java/app/fedilab/android/activities/ShowAccountActivity.java @@ -870,7 +870,7 @@ public class ShowAccountActivity extends BaseActivity implements OnPostActionInt instance = account.getAcct().split("@")[1]; } InstanceNodeInfo instanceNodeInfo = new API(ShowAccountActivity.this).displayNodeInfo(instance); - + String finalInstance = instance; runOnUiThread(new Runnable() { public void run() { if (instanceNodeInfo != null && instanceNodeInfo.getName() != null) { @@ -879,6 +879,15 @@ public class ShowAccountActivity extends BaseActivity implements OnPostActionInt instance_info.setVisibility(View.VISIBLE); TextView seperator = findViewById(R.id.seperator); seperator.setVisibility(View.VISIBLE); + + instance_info.setOnClickListener(v -> { + Intent intent = new Intent(getApplicationContext(), InstanceProfileActivity.class); + Bundle b = new Bundle(); + b.putString("instance", finalInstance); + intent.putExtras(b); + startActivity(intent); + + }); } } }); diff --git a/app/src/main/java/app/fedilab/android/client/API.java b/app/src/main/java/app/fedilab/android/client/API.java index ee37b15c7..e081200a2 100644 --- a/app/src/main/java/app/fedilab/android/client/API.java +++ b/app/src/main/java/app/fedilab/android/client/API.java @@ -1283,8 +1283,7 @@ public class API { e.printStackTrace(); } } - } catch (JSONException ignored) { - } catch (ParseException e) { + } catch (JSONException | ParseException e) { e.printStackTrace(); } return account; @@ -1820,11 +1819,7 @@ public class API { apiResponse.setAccountAdmins(accountAdmins); break; } - } catch (IOException e) { - e.printStackTrace(); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } catch (KeyManagementException e) { + } catch (IOException | NoSuchAlgorithmException | KeyManagementException e) { e.printStackTrace(); } catch (HttpsConnection.HttpsConnectionException e) { setError(e.getStatusCode(), e); @@ -1897,6 +1892,103 @@ public class API { return instanceNodeInfo; } + + + + public InstanceNodeInfo instanceInfo(String domain) { + + String response; + InstanceNodeInfo instanceNodeInfo = null; + try { + response = new HttpsConnection(context, domain).get("https://" + domain + "/.well-known/nodeinfo", 30, null, null); + JSONArray jsonArray = new JSONObject(response).getJSONArray("links"); + ArrayList nodeInfos = new ArrayList<>(); + try { + instanceNodeInfo = new InstanceNodeInfo(); + int i = 0; + while (i < jsonArray.length()) { + + JSONObject resobj = jsonArray.getJSONObject(i); + NodeInfo nodeInfo = new NodeInfo(); + nodeInfo.setHref(resobj.getString("href")); + nodeInfo.setRel(resobj.getString("rel")); + i++; + nodeInfos.add(nodeInfo); + } + if (nodeInfos.size() > 0) { + NodeInfo nodeInfo = nodeInfos.get(nodeInfos.size() - 1); + response = new HttpsConnection(context, this.instance).get(nodeInfo.getHref(), 30, null, null); + JSONObject resobj = new JSONObject(response); + JSONObject jsonObject = resobj.getJSONObject("software"); + String name; + name = jsonObject.getString("name").toUpperCase(); + instanceNodeInfo.setName(name); + instanceNodeInfo.setVersion(jsonObject.getString("version")); + instanceNodeInfo.setOpenRegistrations(resobj.getBoolean("openRegistrations")); + if (name.trim().toUpperCase().compareTo("MASTODON") == 0 || name.trim().toUpperCase().compareTo("PLEROMA") == 0 || name.trim().toUpperCase().compareTo("PIXELFED") == 0){ + APIResponse apiResponse = getInstance(domain); + Instance instanceNode = apiResponse.getInstance(); + instanceNodeInfo.setNodeDescription(instanceNode.getDescription()); + instanceNodeInfo.setNumberOfUsers(instanceNode.getUserCount()); + instanceNodeInfo.setNumberOfPosts(instanceNode.getStatusCount()); + instanceNodeInfo.setNumberOfInstance(instanceNode.getDomainCount()); + instanceNodeInfo.setStaffAccount(instanceNode.getContactAccount()); + instanceNodeInfo.setNodeName(instanceNode.getTitle()); + instanceNodeInfo.setThumbnail(instanceNode.getThumbnail()); + instanceNodeInfo.setVersion(instanceNode.getVersion()); + } + if( resobj.has("metadata")){ + JSONObject metadata = resobj.getJSONObject("metadata"); + if( metadata.has("staffAccounts")){ + instanceNodeInfo.setStaffAccountStr(metadata.getString("staffAccounts")); + } + } + if( instanceNodeInfo.getStaffAccountStr() != null && instanceNodeInfo.getStaffAccount() == null){ + APIResponse search = searchAccounts(instanceNodeInfo.getStaffAccountStr(), 1); + if( search != null && search.getAccounts() != null && search.getAccounts().size() > 0 ){ + instanceNodeInfo.setStaffAccount(search.getAccounts().get(0)); + } + } + } + } catch (JSONException e) { + setDefaultError(e); + } + } catch (IOException | JSONException | NoSuchAlgorithmException | KeyManagementException e) { + e.printStackTrace(); + } catch (HttpsConnection.HttpsConnectionException e){ + APIResponse apiResponse = getInstance(domain); + instanceNodeInfo = new InstanceNodeInfo(); + instanceNodeInfo.setName("MASTODON"); + Instance instanceNode = apiResponse.getInstance(); + instanceNodeInfo.setNodeDescription(instanceNode.getDescription()); + instanceNodeInfo.setNumberOfUsers(instanceNode.getUserCount()); + instanceNodeInfo.setNumberOfPosts(instanceNode.getStatusCount()); + instanceNodeInfo.setNumberOfInstance(instanceNode.getDomainCount()); + instanceNodeInfo.setStaffAccount(instanceNode.getContactAccount()); + instanceNodeInfo.setNodeName(instanceNode.getTitle()); + instanceNodeInfo.setThumbnail(instanceNode.getThumbnail()); + instanceNodeInfo.setVersion(instanceNode.getVersion()); + } + return instanceNodeInfo; + } + + /*** + * Get info on the current Instance *synchronously* + * @return APIResponse + */ + public APIResponse getInstance(String instance) { + try { + String response = new HttpsConnection(context, this.instance).get("https://"+instance+"/api/v1/instance", 30, null, prefKeyOauthTokenT); + Instance instanceEntity = parseInstance(new JSONObject(response)); + apiResponse.setInstance(instanceEntity); + } catch (HttpsConnection.HttpsConnectionException e) { + setError(e.getStatusCode(), e); + } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) { + e.printStackTrace(); + } + return apiResponse; + } + /*** * Get info on the current Instance *synchronously* * @return APIResponse @@ -1908,13 +2000,7 @@ public class API { apiResponse.setInstance(instanceEntity); } catch (HttpsConnection.HttpsConnectionException e) { setError(e.getStatusCode(), e); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } catch (KeyManagementException e) { - e.printStackTrace(); - } catch (JSONException e) { + } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) { e.printStackTrace(); } return apiResponse; @@ -1932,13 +2018,7 @@ public class API { apiResponse.setInstanceRegs(instanceRegs); } catch (HttpsConnection.HttpsConnectionException e) { setError(e.getStatusCode(), e); - } catch (NoSuchAlgorithmException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } catch (KeyManagementException e) { - e.printStackTrace(); - } catch (JSONException e) { + } catch (NoSuchAlgorithmException | IOException | KeyManagementException | JSONException e) { e.printStackTrace(); } return apiResponse; @@ -5569,6 +5649,24 @@ public class API { poll_limits.put("max_expiration", polllimits.getInt("max_expiration")); instance.setPoll_limits(poll_limits); } + if( resobj.has("thumbnail")){ + instance.setThumbnail(resobj.getString("thumbnail")); + } + if( resobj.has("stats")){ + JSONObject stats = resobj.getJSONObject("stats"); + if( stats.has("user_count")) { + instance.setUserCount(stats.getInt("user_count")); + } + if( stats.has("status_count")) { + instance.setStatusCount(stats.getInt("status_count")); + } + if( stats.has("domain_count")) { + instance.setDomainCount(stats.getInt("domain_count")); + } + } + if( resobj.has("contact_account")){ + instance.setContactAccount(parseAccountResponse(context, resobj.getJSONObject("contact_account"))); + } } catch (JSONException e) { e.printStackTrace(); } diff --git a/app/src/main/java/app/fedilab/android/client/Entities/Instance.java b/app/src/main/java/app/fedilab/android/client/Entities/Instance.java index f4b4f4cd7..9914aa863 100644 --- a/app/src/main/java/app/fedilab/android/client/Entities/Instance.java +++ b/app/src/main/java/app/fedilab/android/client/Entities/Instance.java @@ -30,6 +30,11 @@ public class Instance { private String version; private boolean registration; private boolean approval_required; + private Account contactAccount; + private int userCount; + private int statusCount; + private int domainCount; + private String thumbnail; private HashMap poll_limits; @@ -96,4 +101,44 @@ public class Instance { public void setApproval_required(boolean approval_required) { this.approval_required = approval_required; } + + public Account getContactAccount() { + return contactAccount; + } + + public void setContactAccount(Account contactAccount) { + this.contactAccount = contactAccount; + } + + public int getUserCount() { + return userCount; + } + + public void setUserCount(int userCount) { + this.userCount = userCount; + } + + public int getStatusCount() { + return statusCount; + } + + public void setStatusCount(int statusCount) { + this.statusCount = statusCount; + } + + public int getDomainCount() { + return domainCount; + } + + public void setDomainCount(int domainCount) { + this.domainCount = domainCount; + } + + public String getThumbnail() { + return thumbnail; + } + + public void setThumbnail(String thumbnail) { + this.thumbnail = thumbnail; + } } diff --git a/app/src/main/java/app/fedilab/android/client/Entities/InstanceNodeInfo.java b/app/src/main/java/app/fedilab/android/client/Entities/InstanceNodeInfo.java index a04928286..2c963f18c 100644 --- a/app/src/main/java/app/fedilab/android/client/Entities/InstanceNodeInfo.java +++ b/app/src/main/java/app/fedilab/android/client/Entities/InstanceNodeInfo.java @@ -17,9 +17,18 @@ package app.fedilab.android.client.Entities; public class InstanceNodeInfo { private String name; + private String title; private String version; private boolean openRegistrations; private boolean connectionError; + private int numberOfUsers = 0; + private int numberOfPosts = 0; + private int numberOfInstance = 0; + private String staffAccountStr; + private Account staffAccount; + private String nodeName; + private String nodeDescription; + private String thumbnail; public String getName() { return name; @@ -52,4 +61,76 @@ public class InstanceNodeInfo { public void setConnectionError(boolean connectionError) { this.connectionError = connectionError; } + + public int getNumberOfUsers() { + return numberOfUsers; + } + + public void setNumberOfUsers(int numberOfUsers) { + this.numberOfUsers = numberOfUsers; + } + + public int getNumberOfPosts() { + return numberOfPosts; + } + + public void setNumberOfPosts(int numberOfPosts) { + this.numberOfPosts = numberOfPosts; + } + + public String getStaffAccountStr() { + return staffAccountStr; + } + + public void setStaffAccountStr(String staffAccountStr) { + this.staffAccountStr = staffAccountStr; + } + + public Account getStaffAccount() { + return staffAccount; + } + + public void setStaffAccount(Account staffAccount) { + this.staffAccount = staffAccount; + } + + public String getNodeName() { + return nodeName; + } + + public void setNodeName(String nodeName) { + this.nodeName = nodeName; + } + + public String getNodeDescription() { + return nodeDescription; + } + + public void setNodeDescription(String nodeDescription) { + this.nodeDescription = nodeDescription; + } + + public String getThumbnail() { + return thumbnail; + } + + public void setThumbnail(String thumbnail) { + this.thumbnail = thumbnail; + } + + public int getNumberOfInstance() { + return numberOfInstance; + } + + public void setNumberOfInstance(int numberOfInstance) { + this.numberOfInstance = numberOfInstance; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } } diff --git a/app/src/main/res/layout/activity_instance_profile.xml b/app/src/main/res/layout/activity_instance_profile.xml new file mode 100644 index 000000000..d6dde3e49 --- /dev/null +++ b/app/src/main/res/layout/activity_instance_profile.xml @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +