From 17c84f6c37822546a2cd84bd70d6af2e14a3a2e4 Mon Sep 17 00:00:00 2001 From: Thomas Date: Tue, 10 Nov 2020 18:44:48 +0100 Subject: [PATCH] Switch from accounts --- app/src/acad/res/values/strings.xml | 6 +- app/src/full/res/values/strings.xml | 5 +- .../fedilab/fedilabtube/AccountActivity.java | 41 +--- .../app/fedilab/fedilabtube/MainActivity.java | 190 ++++++++++++----- .../fedilabtube/ManageInstancesActivity.java | 46 ++-- .../fedilabtube/client/PeertubeService.java | 4 + .../client/RetrofitPeertubeAPI.java | 19 ++ .../fedilabtube/client/data/InstanceData.java | 92 ++++++++ .../drawer/AboutInstanceAdapter.java | 149 +++++++++++++ .../fedilab/fedilabtube/helper/Helper.java | 22 ++ .../helper/SwitchAccountHelper.java | 80 +++++++ .../fedilab/fedilabtube/sqlite/Sqlite.java | 2 + .../fedilabtube/sqlite/StoredInstanceDAO.java | 196 ++++++++++++++++++ .../fedilabtube/viewmodel/InfoInstanceVM.java | 64 ++++++ .../res/layout/activity_manage_instances.xml | 33 +++ .../main/res/layout/drawer_about_instance.xml | 65 ++++++ app/src/main/res/menu/instance_menu.xml | 9 + app/src/main/res/menu/main_menu.xml | 2 +- 18 files changed, 923 insertions(+), 102 deletions(-) create mode 100644 app/src/main/java/app/fedilab/fedilabtube/drawer/AboutInstanceAdapter.java create mode 100644 app/src/main/java/app/fedilab/fedilabtube/helper/SwitchAccountHelper.java create mode 100644 app/src/main/java/app/fedilab/fedilabtube/sqlite/StoredInstanceDAO.java create mode 100644 app/src/main/java/app/fedilab/fedilabtube/viewmodel/InfoInstanceVM.java create mode 100644 app/src/main/res/layout/drawer_about_instance.xml create mode 100644 app/src/main/res/menu/instance_menu.xml diff --git a/app/src/acad/res/values/strings.xml b/app/src/acad/res/values/strings.xml index 63e9350..17ab7b0 100644 --- a/app/src/acad/res/values/strings.xml +++ b/app/src/acad/res/values/strings.xml @@ -20,7 +20,7 @@ Montrer plus Montrer moins - + Aucune instance ! Verrouillage d\'écran Continuer à lire des vidéos lorsque l\'écran est verrouillé @@ -165,8 +165,12 @@ Logo de l’application Abonnements + Supprimer l\'instance + Etes-vous sûr de vouloir supprimer cette instance ? + Supprimer le commentaire Etes-vous sûr de vouloir supprimer ce commentaire ? + Mode pour les vidéos Filtrer Recherche sépia diff --git a/app/src/full/res/values/strings.xml b/app/src/full/res/values/strings.xml index 0672486..ab9e427 100644 --- a/app/src/full/res/values/strings.xml +++ b/app/src/full/res/values/strings.xml @@ -16,7 +16,7 @@ set_video_sensitive_choice Videos in list Change the layout for displaying videos in a list - + No instances ! Show more Show less @@ -180,6 +180,9 @@ Subscriptions + Delete an instance + Are you sure to delete this instance? + Delete a comment Are you sure to delete this comment? Mode for videos diff --git a/app/src/main/java/app/fedilab/fedilabtube/AccountActivity.java b/app/src/main/java/app/fedilab/fedilabtube/AccountActivity.java index a413d82..2e77ea9 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/AccountActivity.java +++ b/app/src/main/java/app/fedilab/fedilabtube/AccountActivity.java @@ -41,15 +41,13 @@ import com.google.android.material.tabs.TabLayout; import org.jetbrains.annotations.NotNull; -import java.util.List; - import app.fedilab.fedilabtube.client.RetrofitPeertubeAPI; import app.fedilab.fedilabtube.client.data.AccountData.Account; -import app.fedilab.fedilabtube.drawer.OwnAccountsAdapter; import app.fedilab.fedilabtube.fragment.DisplayAccountsFragment; import app.fedilab.fedilabtube.fragment.DisplayChannelsFragment; import app.fedilab.fedilabtube.fragment.DisplayNotificationsFragment; import app.fedilab.fedilabtube.helper.Helper; +import app.fedilab.fedilabtube.helper.SwitchAccountHelper; import app.fedilab.fedilabtube.sqlite.AccountDAO; import app.fedilab.fedilabtube.sqlite.Sqlite; @@ -210,42 +208,7 @@ public class AccountActivity extends AppCompatActivity { finish(); return true; } else if (item.getItemId() == R.id.action_add_account) { - SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); - List accounts = new AccountDAO(AccountActivity.this, db).getAllAccount(); - - AlertDialog.Builder builderSingle = new AlertDialog.Builder(AccountActivity.this); - builderSingle.setTitle(getString(R.string.list_of_accounts)); - if (accounts != null) { - final OwnAccountsAdapter accountsListAdapter = new OwnAccountsAdapter(AccountActivity.this, accounts); - final Account[] accountArray = new Account[accounts.size()]; - int i = 0; - for (Account account : accounts) { - accountArray[i] = account; - i++; - } - builderSingle.setAdapter(accountsListAdapter, (dialog, which) -> { - final Account account = accountArray[which]; - SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE); - SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.PREF_KEY_OAUTH_TOKEN, account.getToken()); - editor.putString(Helper.PREF_INSTANCE, account.getHost()); - editor.putString(Helper.PREF_KEY_ID, account.getId()); - editor.putString(Helper.PREF_KEY_NAME, account.getUsername()); - editor.apply(); - dialog.dismiss(); - Intent intent = new Intent(AccountActivity.this, MainActivity.class); - startActivity(intent); - finish(); - }); - } - builderSingle.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss()); - builderSingle.setPositiveButton(R.string.add_account, (dialog, which) -> { - Intent intent = new Intent(AccountActivity.this, LoginActivity.class); - startActivity(intent); - finish(); - }); - - builderSingle.show(); + SwitchAccountHelper.switchDialog(AccountActivity.this, true); } return super.onOptionsItemSelected(item); } diff --git a/app/src/main/java/app/fedilab/fedilabtube/MainActivity.java b/app/src/main/java/app/fedilab/fedilabtube/MainActivity.java index ca72939..7e8f2e2 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/MainActivity.java +++ b/app/src/main/java/app/fedilab/fedilabtube/MainActivity.java @@ -39,7 +39,6 @@ import androidx.appcompat.widget.SearchView; import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; -import androidx.lifecycle.ViewModelProvider; import com.google.android.material.bottomnavigation.BottomNavigationView; import com.kobakei.ratethisapp.RateThisApp; @@ -55,7 +54,7 @@ import java.util.regex.Pattern; import app.fedilab.fedilabtube.client.RetrofitPeertubeAPI; import app.fedilab.fedilabtube.client.data.AccountData.Account; -import app.fedilab.fedilabtube.client.data.VideoData; +import app.fedilab.fedilabtube.client.data.InstanceData; import app.fedilab.fedilabtube.client.entities.Error; import app.fedilab.fedilabtube.client.entities.OauthParams; import app.fedilab.fedilabtube.client.entities.PeertubeInformation; @@ -66,13 +65,16 @@ import app.fedilab.fedilabtube.client.entities.WellKnownNodeinfo; import app.fedilab.fedilabtube.fragment.DisplayOverviewFragment; import app.fedilab.fedilabtube.fragment.DisplayVideosFragment; import app.fedilab.fedilabtube.helper.Helper; +import app.fedilab.fedilabtube.helper.SwitchAccountHelper; import app.fedilab.fedilabtube.services.RetrieveInfoService; import app.fedilab.fedilabtube.sqlite.AccountDAO; import app.fedilab.fedilabtube.sqlite.Sqlite; -import app.fedilab.fedilabtube.viewmodel.SearchVM; +import app.fedilab.fedilabtube.sqlite.StoredInstanceDAO; import app.fedilab.fedilabtube.viewmodel.TimelineVM; import es.dmoral.toasty.Toasty; +import static app.fedilab.fedilabtube.MainActivity.TypeOfConnection.NORMAL; +import static app.fedilab.fedilabtube.MainActivity.TypeOfConnection.SURFING; import static app.fedilab.fedilabtube.helper.Helper.academies; public class MainActivity extends AppCompatActivity { @@ -80,12 +82,20 @@ public class MainActivity extends AppCompatActivity { public static PeertubeInformation peertubeInformation; public static int PICK_INSTANCE = 5641; + public static int PICK_INSTANCE_SURF = 5642; final FragmentManager fm = getSupportFragmentManager(); Fragment active; private DisplayVideosFragment recentFragment, locaFragment, trendingFragment, subscriptionFragment, mostLikedFragment; private DisplayOverviewFragment overviewFragment; public static UserMe userMe; + public enum TypeOfConnection{ + UNKNOWN, + NORMAL, + SURFING + } + + private TypeOfConnection typeOfConnection; private final BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener = item -> { @@ -136,6 +146,7 @@ public class MainActivity extends AppCompatActivity { Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); + typeOfConnection = TypeOfConnection.UNKNOWN; BottomNavigationView navView = findViewById(R.id.nav_view); @@ -144,6 +155,7 @@ public class MainActivity extends AppCompatActivity { if( getSupportActionBar() != null) { getSupportActionBar().setDisplayShowTitleEnabled(false); } + checkIfConnectedUsers(); Fragment fragment = getSupportFragmentManager().findFragmentByTag("5"); if (fragment != null) @@ -212,15 +224,6 @@ public class MainActivity extends AppCompatActivity { if (Helper.isLoggedIn(MainActivity.this)) { - if( BuildConfig.full_instances) { - ImageButton instances = toolbar.findViewById(R.id.instances); - instances.setVisibility(View.VISIBLE); - instances.setOnClickListener(v->{ - Intent intent = new Intent(MainActivity.this, ManageInstancesActivity.class); - startActivity(intent); - overridePendingTransition( R.anim.slide_in_up, R.anim.slide_out_up ); - }); - } navView.inflateMenu(R.menu.bottom_nav_menu_connected); new Thread(() -> { final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); @@ -353,7 +356,7 @@ public class MainActivity extends AppCompatActivity { return false; } }); - MenuItem instanceItem = menu.findItem(R.id.action_change_instance); + MenuItem uploadItem = menu.findItem(R.id.action_upload); MenuItem myVideosItem = menu.findItem(R.id.action_myvideos); MenuItem playslistItem = menu.findItem(R.id.action_playlist); @@ -362,40 +365,104 @@ public class MainActivity extends AppCompatActivity { MenuItem settingsItem = menu.findItem(R.id.action_settings); MenuItem sepiaSearchItem = menu.findItem(R.id.action_sepia_search); MenuItem incognitoItem = menu.findItem(R.id.action_incognito); - if (Helper.isLoggedIn(MainActivity.this)) { - instanceItem.setVisible(false); - uploadItem.setVisible(true); - myVideosItem.setVisible(true); - playslistItem.setVisible(true); - historyItem.setVisible(true); - settingsItem.setVisible(false); - mostLikedItem.setVisible(true); - incognitoItem.setVisible(true); - final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); - boolean checked = sharedpreferences.getBoolean(getString(R.string.set_store_in_history), true); - incognitoItem.setChecked(checked); - } else { - instanceItem.setVisible(true); - uploadItem.setVisible(false); - myVideosItem.setVisible(false); - playslistItem.setVisible(false); - historyItem.setVisible(false); - settingsItem.setVisible(true); - mostLikedItem.setVisible(false); - incognitoItem.setVisible(false); + MenuItem instanceItem = menu.findItem(R.id.action_change_instance); + MenuItem accountItem = menu.findItem(R.id.action_account); + + Toolbar toolbar = findViewById(R.id.toolbar); + ImageButton instances = toolbar.findViewById(R.id.instances); + if(BuildConfig.full_instances && ((Helper.isLoggedIn(MainActivity.this) && typeOfConnection == NORMAL) || typeOfConnection == SURFING)) { + instances.setVisibility(View.VISIBLE); + instances.setOnClickListener(null); + instances.setOnClickListener(v->{ + Intent intent = new Intent(MainActivity.this, ManageInstancesActivity.class); + startActivity(intent); + overridePendingTransition( R.anim.slide_in_up, R.anim.slide_out_up ); + }); + }else { + instances.setVisibility(View.GONE); } + switch (typeOfConnection){ + case UNKNOWN: + instanceItem.setVisible(false); + accountItem.setVisible(false); + uploadItem.setVisible(false); + myVideosItem.setVisible(false); + playslistItem.setVisible(false); + historyItem.setVisible(false); + settingsItem.setVisible(false); + mostLikedItem.setVisible(false); + incognitoItem.setVisible(false); + break; + case NORMAL: + accountItem.setVisible(true); + if (Helper.isLoggedIn(MainActivity.this)) { + instanceItem.setVisible(false); + uploadItem.setVisible(true); + myVideosItem.setVisible(true); + playslistItem.setVisible(true); + historyItem.setVisible(true); + settingsItem.setVisible(false); + mostLikedItem.setVisible(true); + incognitoItem.setVisible(true); + final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + boolean checked = sharedpreferences.getBoolean(getString(R.string.set_store_in_history), true); + incognitoItem.setChecked(checked); + } else { + instanceItem.setVisible(true); + uploadItem.setVisible(false); + myVideosItem.setVisible(false); + playslistItem.setVisible(false); + historyItem.setVisible(false); + settingsItem.setVisible(true); + mostLikedItem.setVisible(false); + incognitoItem.setVisible(false); + } + break; + case SURFING: + instanceItem.setVisible(false); + accountItem.setVisible(true); + uploadItem.setVisible(false); + myVideosItem.setVisible(false); + playslistItem.setVisible(false); + historyItem.setVisible(false); + settingsItem.setVisible(false); + mostLikedItem.setVisible(false); + incognitoItem.setVisible(false); + break; + } + + if (!BuildConfig.full_instances) { sepiaSearchItem.setVisible(false); } return true; } + private void checkIfConnectedUsers(){ + new Thread(() -> { + try { + typeOfConnection = NORMAL; + if( !Helper.isLoggedIn(MainActivity.this)) { + SQLiteDatabase db = Sqlite.getInstance(getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + List accounts = new AccountDAO(MainActivity.this, db).getAllAccount(); + if( accounts != null && accounts.size() > 0 ) { + //The user is not authenticated and there accounts in db. That means the user is surfing some other instances + typeOfConnection = TypeOfConnection.SURFING; + } + } + runOnUiThread(this::invalidateOptionsMenu); + + } catch (Exception e) { + e.printStackTrace(); + } + }).start(); + } @Override public boolean onOptionsItemSelected(MenuItem item) { if (item.getItemId() == R.id.action_change_instance) { if (BuildConfig.full_instances) { - showRadioButtonDialogFullInstances(MainActivity.this); + showRadioButtonDialogFullInstances(MainActivity.this, false); } else { showRadioButtonDialog(); } @@ -405,12 +472,16 @@ public class MainActivity extends AppCompatActivity { startActivity(intent); } else if (item.getItemId() == R.id.action_account) { Intent intent; - if (Helper.isLoggedIn(MainActivity.this)) { - intent = new Intent(MainActivity.this, AccountActivity.class); + if( typeOfConnection == SURFING) { + SwitchAccountHelper.switchDialog(MainActivity.this, false); } else { - intent = new Intent(MainActivity.this, LoginActivity.class); + if (Helper.isLoggedIn(MainActivity.this)) { + intent = new Intent(MainActivity.this, AccountActivity.class); + } else { + intent = new Intent(MainActivity.this, LoginActivity.class); + } + startActivity(intent); } - startActivity(intent); return true; } else if (item.getItemId() == R.id.action_upload) { Intent intent = new Intent(MainActivity.this, PeertubeUploadActivity.class); @@ -519,8 +590,10 @@ public class MainActivity extends AppCompatActivity { alert.show(); } + + @SuppressLint("ApplySharedPref") - public static void showRadioButtonDialogFullInstances(Activity activity) { + public static void showRadioButtonDialogFullInstances(Activity activity, boolean storeInDb) { final SharedPreferences sharedpreferences = activity.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); AlertDialog.Builder alt_bld = new AlertDialog.Builder(activity); alt_bld.setTitle(R.string.instance_choice); @@ -541,11 +614,22 @@ public class MainActivity extends AppCompatActivity { SharedPreferences.Editor editor = sharedpreferences.edit(); editor.putString(Helper.PREF_INSTANCE, newInstance); editor.commit(); - activity.runOnUiThread(() -> { - dialog.dismiss(); - Intent intent = new Intent(activity, MainActivity.class); - activity.startActivity(intent); - }); + if( storeInDb) { + newInstance = newInstance.trim().toLowerCase(); + InstanceData.AboutInstance aboutInstance = new RetrofitPeertubeAPI(activity, newInstance, null).getAboutInstance(); + SQLiteDatabase db = Sqlite.getInstance(activity.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + new StoredInstanceDAO(activity, db).insertInstance(aboutInstance, newInstance); + activity.runOnUiThread(() -> { + dialog.dismiss(); + Helper.logoutNoRemoval(activity); + }); + }else { + activity.runOnUiThread(() -> { + dialog.dismiss(); + Intent intent = new Intent(activity, MainActivity.class); + activity.startActivity(intent); + }); + } } else { activity.runOnUiThread(() -> Toasty.error(activity, activity.getString(R.string.not_valide_instance), Toast.LENGTH_LONG).show()); } @@ -557,7 +641,11 @@ public class MainActivity extends AppCompatActivity { alt_bld.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss()); alt_bld.setNeutralButton(R.string.help, (dialog, which) -> { Intent intent = new Intent(activity, InstancePickerActivity.class); - activity.startActivityForResult(intent, PICK_INSTANCE); + if(storeInDb) { + activity.startActivityForResult(intent, PICK_INSTANCE_SURF); + }else{ + activity.startActivityForResult(intent, PICK_INSTANCE); + } }); AlertDialog alert = alt_bld.create(); alert.show(); @@ -574,9 +662,17 @@ public class MainActivity extends AppCompatActivity { SharedPreferences.Editor editor = sharedpreferences.edit(); editor.putString(Helper.PREF_INSTANCE, String.valueOf(data.getData())); editor.commit(); - finish(); } + }else if (requestCode == PICK_INSTANCE_SURF && resultCode == Activity.RESULT_OK) { + if (data != null && data.getData() != null) { + final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedpreferences.edit(); + editor.putString(Helper.PREF_INSTANCE, String.valueOf(data.getData())); + editor.commit(); + + finish(); + } } } } \ No newline at end of file diff --git a/app/src/main/java/app/fedilab/fedilabtube/ManageInstancesActivity.java b/app/src/main/java/app/fedilab/fedilabtube/ManageInstancesActivity.java index c35582d..ed99941 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/ManageInstancesActivity.java +++ b/app/src/main/java/app/fedilab/fedilabtube/ManageInstancesActivity.java @@ -16,24 +16,28 @@ package app.fedilab.fedilabtube; import android.annotation.SuppressLint; import android.app.Activity; -import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.os.Bundle; import android.view.MenuItem; import android.view.View; import androidx.appcompat.app.AppCompatActivity; +import androidx.lifecycle.ViewModelProvider; +import java.util.List; + +import app.fedilab.fedilabtube.client.data.InstanceData; import app.fedilab.fedilabtube.databinding.ActivityManageInstancesBinding; +import app.fedilab.fedilabtube.drawer.AboutInstanceAdapter; import app.fedilab.fedilabtube.helper.Helper; +import app.fedilab.fedilabtube.viewmodel.InfoInstanceVM; -import static app.fedilab.fedilabtube.MainActivity.PICK_INSTANCE; +import static app.fedilab.fedilabtube.MainActivity.PICK_INSTANCE_SURF; import static app.fedilab.fedilabtube.MainActivity.showRadioButtonDialogFullInstances; -public class ManageInstancesActivity extends AppCompatActivity { +public class ManageInstancesActivity extends AppCompatActivity implements AboutInstanceAdapter.AllInstancesRemoved { private ActivityManageInstancesBinding binding; @@ -47,8 +51,24 @@ public class ManageInstancesActivity extends AppCompatActivity { getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true); } + binding.loader.setVisibility(View.VISIBLE); + binding.noAction.setVisibility(View.GONE); + binding.lvInstances.setVisibility(View.GONE); + binding.actionButton.setOnClickListener(v-> showRadioButtonDialogFullInstances(ManageInstancesActivity.this, true)); - binding.actionButton.setOnClickListener(v-> showRadioButtonDialogFullInstances(ManageInstancesActivity.this)); + InfoInstanceVM viewModelInfoInstance = new ViewModelProvider(ManageInstancesActivity.this).get(InfoInstanceVM.class); + viewModelInfoInstance.getInstances().observe(ManageInstancesActivity.this, this::manageVIewInfoInstance); + } + + private void manageVIewInfoInstance(List aboutInstances) { + binding.loader.setVisibility(View.GONE); + if( aboutInstances == null || aboutInstances.size() == 0) { + binding.noAction.setVisibility(View.VISIBLE); + binding.lvInstances.setVisibility(View.GONE); + return; + } + binding.noAction.setVisibility(View.GONE); + binding.lvInstances.setVisibility(View.VISIBLE); } @@ -72,16 +92,16 @@ public class ManageInstancesActivity extends AppCompatActivity { @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); - if (requestCode == PICK_INSTANCE && resultCode == Activity.RESULT_OK) { + if (requestCode == PICK_INSTANCE_SURF && resultCode == Activity.RESULT_OK) { if (data != null && data.getData() != null) { - final SharedPreferences sharedpreferences = getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); - SharedPreferences.Editor editor = sharedpreferences.edit(); - editor.putString(Helper.PREF_INSTANCE, String.valueOf(data.getData())); - editor.commit(); - Intent intent = new Intent(ManageInstancesActivity.this, MainActivity.class); - startActivity(intent); - finish(); + Helper.logoutNoRemoval(ManageInstancesActivity.this); } } } + + @Override + public void onAllInstancesRemoved() { + binding.noAction.setVisibility(View.VISIBLE); + binding.lvInstances.setVisibility(View.GONE); + } } diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/PeertubeService.java b/app/src/main/java/app/fedilab/fedilabtube/client/PeertubeService.java index 0a89718..ed9d723 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/client/PeertubeService.java +++ b/app/src/main/java/app/fedilab/fedilabtube/client/PeertubeService.java @@ -66,6 +66,10 @@ public interface PeertubeService { @GET(".well-known/nodeinfo") Call getWellKnownNodeinfo(); + //Instance info + @GET("config/about") + Call configAbout(); + @GET("{nodeInfoPath}") Call getNodeinfo(@Path(value = "nodeInfoPath", encoded = true) String nodeInfoPath); diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/RetrofitPeertubeAPI.java b/app/src/main/java/app/fedilab/fedilabtube/client/RetrofitPeertubeAPI.java index 588e06e..522e876 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/client/RetrofitPeertubeAPI.java +++ b/app/src/main/java/app/fedilab/fedilabtube/client/RetrofitPeertubeAPI.java @@ -597,6 +597,25 @@ public class RetrofitPeertubeAPI { return apiResponse; } + /** + * About the instance + * @return AboutInstance + */ + public InstanceData.AboutInstance getAboutInstance() { + + PeertubeService peertubeService = init(); + Call about = peertubeService.configAbout(); + try { + Response response = about.execute(); + if (response.isSuccessful() && response.body() != null) { + return response.body(); + } + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + /** * Returns informations about Peertube such privacies, licenses, etc. * diff --git a/app/src/main/java/app/fedilab/fedilabtube/client/data/InstanceData.java b/app/src/main/java/app/fedilab/fedilabtube/client/data/InstanceData.java index 69c4fb7..8a77425 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/client/data/InstanceData.java +++ b/app/src/main/java/app/fedilab/fedilabtube/client/data/InstanceData.java @@ -1,9 +1,12 @@ package app.fedilab.fedilabtube.client.data; +import android.os.Parcel; +import android.os.Parcelable; import android.text.SpannableStringBuilder; import com.google.gson.annotations.SerializedName; +import java.io.Serializable; import java.util.Date; import java.util.List; @@ -239,5 +242,94 @@ public class InstanceData { } } + public static class AboutInstance implements Parcelable, Serializable { + + @SerializedName("name") + private String name; + @SerializedName("shortDescription") + private String shortDescription; + @SerializedName("description") + private String description; + @SerializedName("terms") + private String terms; + private String host; + + + public AboutInstance(){} + + protected AboutInstance(Parcel in) { + name = in.readString(); + shortDescription = in.readString(); + description = in.readString(); + terms = in.readString(); + host = in.readString(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public AboutInstance createFromParcel(Parcel in) { + return new AboutInstance(in); + } + + @Override + public AboutInstance[] newArray(int size) { + return new AboutInstance[size]; + } + }; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getShortDescription() { + return shortDescription; + } + + public void setShortDescription(String shortDescription) { + this.shortDescription = shortDescription; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getTerms() { + return terms; + } + + public void setTerms(String terms) { + this.terms = terms; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int i) { + parcel.writeString(name); + parcel.writeString(shortDescription); + parcel.writeString(description); + parcel.writeString(terms); + parcel.writeString(host); + } + } } diff --git a/app/src/main/java/app/fedilab/fedilabtube/drawer/AboutInstanceAdapter.java b/app/src/main/java/app/fedilab/fedilabtube/drawer/AboutInstanceAdapter.java new file mode 100644 index 0000000..18ff0c4 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/drawer/AboutInstanceAdapter.java @@ -0,0 +1,149 @@ +package app.fedilab.fedilabtube.drawer; +/* Copyright 2020 Thomas Schneider + * + * This file is a part of TubeLab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * TubeLab 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 General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with TubeLab; if not, + * see . */ + + +import android.annotation.SuppressLint; +import android.app.Activity; +import android.app.AlertDialog; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.database.sqlite.SQLiteDatabase; +import android.os.Handler; +import android.os.Looper; +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.appcompat.widget.PopupMenu; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.List; + +import app.fedilab.fedilabtube.MainActivity; +import app.fedilab.fedilabtube.R; +import app.fedilab.fedilabtube.client.RetrofitPeertubeAPI; +import app.fedilab.fedilabtube.client.data.InstanceData; +import app.fedilab.fedilabtube.databinding.DrawerAboutInstanceBinding; +import app.fedilab.fedilabtube.helper.Helper; +import app.fedilab.fedilabtube.sqlite.Sqlite; +import app.fedilab.fedilabtube.sqlite.StoredInstanceDAO; + + +public class AboutInstanceAdapter extends RecyclerView.Adapter { + + + private final List aboutInstances; + private final AboutInstanceAdapter aboutInstanceAdapter; + public AllInstancesRemoved allInstancesRemoved; + private Context context; + + public AboutInstanceAdapter(List aboutInstances) { + aboutInstanceAdapter = this; + this.aboutInstances = aboutInstances; + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public int getItemCount() { + return aboutInstances.size(); + } + + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + context = parent.getContext(); + DrawerAboutInstanceBinding itemBinding = DrawerAboutInstanceBinding .inflate(LayoutInflater.from(parent.getContext()), parent, false); + return new ViewHolder(itemBinding); + } + + @SuppressLint("ApplySharedPref") + @Override + public void onBindViewHolder(@NonNull final RecyclerView.ViewHolder viewHolder, int i) { + + context = viewHolder.itemView.getContext(); + + final ViewHolder holder = (ViewHolder) viewHolder; + + final InstanceData.AboutInstance aboutInstance = aboutInstances.get(i); + + holder.binding.aboutInstanceDescription.setText(aboutInstance.getShortDescription()); + holder.binding.aboutInstanceName.setText(aboutInstance.getName()); + holder.binding.playlistContainer.setOnClickListener(v->{ + final SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedpreferences.edit(); + editor.putString(Helper.PREF_INSTANCE, aboutInstance.getHost()); + editor.commit(); + ((Activity)context).runOnUiThread(() -> { + Intent intent = new Intent(context, MainActivity.class); + context.startActivity(intent); + }); + }); + holder.binding.instanceMore.setOnClickListener(v->{ + PopupMenu popup = new PopupMenu(context, holder.binding.instanceMore); + popup.getMenuInflater() + .inflate(R.menu.instance_menu, popup.getMenu()); + popup.setOnMenuItemClickListener(item -> { + int itemId = item.getItemId(); + if (itemId == R.id.action_delete) { + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(R.string.delete_instance); + builder.setMessage(R.string.delete_instance_confirm); + builder.setIcon(android.R.drawable.ic_dialog_alert) + .setPositiveButton(R.string.delete, (dialog, which) -> { + new Thread(() -> { + SQLiteDatabase db = Sqlite.getInstance(context.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + new StoredInstanceDAO(context, db).removeInstance(aboutInstance.getHost()); + Handler mainHandler = new Handler(Looper.getMainLooper()); + Runnable myRunnable = () -> { + if (aboutInstances.size() == 0) { + allInstancesRemoved.onAllInstancesRemoved(); + } + }; + mainHandler.post(myRunnable); + }).start(); + + dialog.dismiss(); + }) + .setNegativeButton(R.string.no, (dialog, which) -> dialog.dismiss()) + .show(); + } + return true; + }); + }); + } + + public interface AllInstancesRemoved { + void onAllInstancesRemoved(); + } + + static class ViewHolder extends RecyclerView.ViewHolder { + + DrawerAboutInstanceBinding binding; + + ViewHolder(DrawerAboutInstanceBinding itemView) { + super(itemView.getRoot()); + binding = itemView; + } + + } + +} \ No newline at end of file diff --git a/app/src/main/java/app/fedilab/fedilabtube/helper/Helper.java b/app/src/main/java/app/fedilab/fedilabtube/helper/Helper.java index 6b60653..4373ec1 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/helper/Helper.java +++ b/app/src/main/java/app/fedilab/fedilabtube/helper/Helper.java @@ -121,6 +121,7 @@ public class Helper { public static final String PREF_KEY_ID = "userID"; public static final String PREF_KEY_NAME = "my_user_name"; public static final String PREF_INSTANCE = "instance"; + public static final String PREF_INSTANCE_SURF = "instance_surf"; public static final int EXTERNAL_STORAGE_REQUEST_CODE = 84; public static final String SET_VIDEOS_PER_PAGE = "set_videos_per_page"; public static final String VIDEO_ID = "video_id_update"; @@ -626,6 +627,27 @@ public class Helper { } + /** + * Log out without removing user in db + * + * @param activity Activity + */ + public static void logoutNoRemoval(Activity activity) { + SharedPreferences sharedpreferences = activity.getSharedPreferences(APP_PREFS, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedpreferences.edit(); + editor.putString(PREF_KEY_OAUTH_TOKEN, null); + editor.putString(CLIENT_ID, null); + editor.putString(CLIENT_SECRET, null); + editor.putString(PREF_KEY_ID, null); + editor.putString(PREF_INSTANCE, null); + editor.putString(ID, null); + editor.apply(); + Intent loginActivity = new Intent(activity, MainActivity.class); + activity.startActivity(loginActivity); + activity.finish(); + } + + public static int getAttColor(Context context, int attColor) { TypedValue typedValue = new TypedValue(); context.getTheme().resolveAttribute(attColor, typedValue, true); diff --git a/app/src/main/java/app/fedilab/fedilabtube/helper/SwitchAccountHelper.java b/app/src/main/java/app/fedilab/fedilabtube/helper/SwitchAccountHelper.java new file mode 100644 index 0000000..e233377 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/helper/SwitchAccountHelper.java @@ -0,0 +1,80 @@ +package app.fedilab.fedilabtube.helper; +/* Copyright 2020 Thomas Schneider + * + * This file is a part of TubeLab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * TubeLab 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 General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with TubeLab; if not, + * see . */ + +import android.app.Activity; +import android.content.Intent; +import android.content.SharedPreferences; +import android.database.sqlite.SQLiteDatabase; + +import androidx.appcompat.app.AlertDialog; + +import java.util.List; + + +import app.fedilab.fedilabtube.LoginActivity; +import app.fedilab.fedilabtube.MainActivity; +import app.fedilab.fedilabtube.R; +import app.fedilab.fedilabtube.client.data.AccountData; +import app.fedilab.fedilabtube.drawer.OwnAccountsAdapter; +import app.fedilab.fedilabtube.sqlite.AccountDAO; +import app.fedilab.fedilabtube.sqlite.Sqlite; + +import static android.content.Context.MODE_PRIVATE; + +public class SwitchAccountHelper { + + + public static void switchDialog(Activity activity, boolean withAddAccount){ + SQLiteDatabase db = Sqlite.getInstance(activity.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + List accounts = new AccountDAO(activity, db).getAllAccount(); + + AlertDialog.Builder builderSingle = new AlertDialog.Builder(activity); + builderSingle.setTitle(activity.getString(R.string.list_of_accounts)); + if (accounts != null) { + final OwnAccountsAdapter accountsListAdapter = new OwnAccountsAdapter(activity, accounts); + final AccountData.Account[] accountArray = new AccountData.Account[accounts.size()]; + int i = 0; + for (AccountData.Account account : accounts) { + accountArray[i] = account; + i++; + } + builderSingle.setAdapter(accountsListAdapter, (dialog, which) -> { + final AccountData.Account account = accountArray[which]; + SharedPreferences sharedpreferences = activity.getSharedPreferences(Helper.APP_PREFS, MODE_PRIVATE); + SharedPreferences.Editor editor = sharedpreferences.edit(); + editor.putString(Helper.PREF_KEY_OAUTH_TOKEN, account.getToken()); + editor.putString(Helper.PREF_INSTANCE, account.getHost()); + editor.putString(Helper.PREF_KEY_ID, account.getId()); + editor.putString(Helper.PREF_KEY_NAME, account.getUsername()); + editor.apply(); + dialog.dismiss(); + Intent intent = new Intent(activity, MainActivity.class); + activity.startActivity(intent); + activity.finish(); + }); + } + builderSingle.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss()); + if( withAddAccount) { + builderSingle.setPositiveButton(R.string.add_account, (dialog, which) -> { + Intent intent = new Intent(activity, LoginActivity.class); + activity.startActivity(intent); + activity.finish(); + }); + } + + builderSingle.show(); + } +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/sqlite/Sqlite.java b/app/src/main/java/app/fedilab/fedilabtube/sqlite/Sqlite.java index f3efd37..e9c2fb0 100644 --- a/app/src/main/java/app/fedilab/fedilabtube/sqlite/Sqlite.java +++ b/app/src/main/java/app/fedilab/fedilabtube/sqlite/Sqlite.java @@ -85,11 +85,13 @@ public class Sqlite extends SQLiteOpenHelper { static final String COL_USER_INSTANCE = "USER_INSTANCE"; static final String TABLE_BOOKMARKED_INSTANCES = "BOOKMARKED_INSTANCES"; + static final String COL_ABOUT = "ABOUT"; private final String CREATE_TABLE_STORED_INSTANCES = "CREATE TABLE " + TABLE_BOOKMARKED_INSTANCES + "(" + COL_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + COL_INSTANCE + " TEXT NOT NULL, " + COL_USER_ID + " TEXT NOT NULL, " + + COL_ABOUT + " TEXT NOT NULL, " + COL_USER_INSTANCE + " TEXT NOT NULL)"; public Sqlite(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { diff --git a/app/src/main/java/app/fedilab/fedilabtube/sqlite/StoredInstanceDAO.java b/app/src/main/java/app/fedilab/fedilabtube/sqlite/StoredInstanceDAO.java new file mode 100644 index 0000000..55bb3ae --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/sqlite/StoredInstanceDAO.java @@ -0,0 +1,196 @@ +package app.fedilab.fedilabtube.sqlite; +/* Copyright 2020 Thomas Schneider + * + * This file is a part of TubeLab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * TubeLab 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 General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with TubeLab; if not, + * see . */ + +import android.content.ContentValues; +import android.content.Context; +import android.content.SharedPreferences; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; + +import com.google.gson.Gson; + +import java.util.ArrayList; +import java.util.List; + +import app.fedilab.fedilabtube.client.data.InstanceData; +import app.fedilab.fedilabtube.helper.Helper; + + +@SuppressWarnings("UnusedReturnValue") +public class StoredInstanceDAO { + + private final SQLiteDatabase db; + public Context context; + + + public StoredInstanceDAO(Context context, SQLiteDatabase db) { + //Creation of the DB with tables + this.context = context; + this.db = db; + } + + + /** + * Insert instance info in database + * + * @param aboutInstance AboutInstance + * @param targetedInstance String + * @return boolean + */ + public boolean insertInstance(InstanceData.AboutInstance aboutInstance, String targetedInstance) { + ContentValues values = new ContentValues(); + + SharedPreferences sharedpreferences = context.getSharedPreferences(Helper.APP_PREFS, Context.MODE_PRIVATE); + String userId = sharedpreferences.getString(Helper.PREF_KEY_ID, null); + String instance = Helper.getLiveInstance(context); + + values.put(Sqlite.COL_USER_ID, userId); + values.put(Sqlite.COL_USER_INSTANCE, instance); + values.put(Sqlite.COL_ABOUT, aboutInstanceToStringStorage(aboutInstance)); + values.put(Sqlite.COL_INSTANCE, targetedInstance); + //Inserts account + try { + db.insertOrThrow(Sqlite.TABLE_BOOKMARKED_INSTANCES, null, values); + + } catch (Exception e) { + e.printStackTrace(); + return false; + } + return true; + } + + /** + * Unserialized AboutInstance + * + * @param serializedAboutInstance String serialized AboutInstance + * @return AboutInstance + */ + public static InstanceData.AboutInstance restoreAboutInstanceFromString(String serializedAboutInstance) { + Gson gson = new Gson(); + try { + return gson.fromJson(serializedAboutInstance, InstanceData.AboutInstance.class); + } catch (Exception e) { + return null; + } + } + + /** + * Serialized AboutInstance class + * + * @param aboutInstance AboutInstance to serialize + * @return String serialized AboutInstance + */ + public static String aboutInstanceToStringStorage(InstanceData.AboutInstance aboutInstance) { + Gson gson = new Gson(); + try { + return gson.toJson(aboutInstance); + } catch (Exception e) { + return null; + } + } + + /** + * Insert instance info in database + * + * @param aboutInstance AboutInstance + * @param targetedInstance String + * @return int + */ + public int updateInstance(InstanceData.AboutInstance aboutInstance, String targetedInstance) { + ContentValues values = new ContentValues(); + values.put(Sqlite.COL_ABOUT, aboutInstanceToStringStorage(aboutInstance)); + try { + return db.update(Sqlite.TABLE_BOOKMARKED_INSTANCES, + values, Sqlite.COL_INSTANCE + " = ?", + new String[]{targetedInstance}); + } catch (Exception e) { + e.printStackTrace(); + return -1; + } + + } + + + public int removeInstance(String instance) { + return db.delete(Sqlite.TABLE_BOOKMARKED_INSTANCES, Sqlite.COL_INSTANCE + " = '" +instance + "'", null); + } + + + + /** + * Returns all Instance in db + * + * @return List + */ + public List getAllInstances() { + + try { + Cursor c = db.query(Sqlite.TABLE_BOOKMARKED_INSTANCES, null, null, null, null, null, Sqlite.COL_INSTANCE + " ASC", null); + return cursorToListInstances(c); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } + + + + /*** + * Method to hydrate an Account from database + * @param c Cursor + * @return Account + */ + private InstanceData.AboutInstance cursorToInstance(Cursor c) { + //No element found + if (c.getCount() == 0) { + c.close(); + return null; + } + //Take the first element + c.moveToFirst(); + //New user + String aboutInstanceStr = c.getString(c.getColumnIndex(Sqlite.COL_ABOUT)); + InstanceData.AboutInstance aboutInstance = restoreAboutInstanceFromString(aboutInstanceStr); + //Close the cursor + c.close(); + return aboutInstance; + } + + + /*** + * Method to hydrate an AboutInstance from database + * @param c Cursor + * @return List + */ + private List cursorToListInstances(Cursor c) { + //No element found + if (c.getCount() == 0) { + c.close(); + return null; + } + List aboutInstances = new ArrayList<>(); + while (c.moveToNext()) { + String aboutInstanceStr = c.getString(c.getColumnIndex(Sqlite.COL_ABOUT)); + InstanceData.AboutInstance aboutInstance = restoreAboutInstanceFromString(aboutInstanceStr); + aboutInstances.add(aboutInstance); + } + //Close the cursor + c.close(); + return aboutInstances; + } + + +} diff --git a/app/src/main/java/app/fedilab/fedilabtube/viewmodel/InfoInstanceVM.java b/app/src/main/java/app/fedilab/fedilabtube/viewmodel/InfoInstanceVM.java new file mode 100644 index 0000000..a6e5185 --- /dev/null +++ b/app/src/main/java/app/fedilab/fedilabtube/viewmodel/InfoInstanceVM.java @@ -0,0 +1,64 @@ +package app.fedilab.fedilabtube.viewmodel; +/* Copyright 2020 Thomas Schneider + * + * This file is a part of TubeLab + * + * This program is free software; you can redistribute it and/or modify it under the terms of the + * GNU General Public License as published by the Free Software Foundation; either version 3 of the + * License, or (at your option) any later version. + * + * TubeLab 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 General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along with TubeLab; if not, + * see . */ + +import android.app.Application; +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.os.Handler; +import android.os.Looper; + +import androidx.annotation.NonNull; +import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; + +import java.util.List; + +import app.fedilab.fedilabtube.client.data.InstanceData; +import app.fedilab.fedilabtube.sqlite.Sqlite; +import app.fedilab.fedilabtube.sqlite.StoredInstanceDAO; + + +public class InfoInstanceVM extends AndroidViewModel { + private MutableLiveData> aboutInstanceMutableLiveData; + + public InfoInstanceVM(@NonNull Application application) { + super(application); + } + + public LiveData> getInstances() { + aboutInstanceMutableLiveData = new MutableLiveData<>(); + loadInstances(); + return aboutInstanceMutableLiveData; + } + + + private void loadInstances() { + Context _mContext = getApplication().getApplicationContext(); + new Thread(() -> { + try { + SQLiteDatabase db = Sqlite.getInstance(_mContext.getApplicationContext(), Sqlite.DB_NAME, null, Sqlite.DB_VERSION).open(); + List instances = new StoredInstanceDAO(_mContext, db).getAllInstances(); + Handler mainHandler = new Handler(Looper.getMainLooper()); + Runnable myRunnable = () -> aboutInstanceMutableLiveData.setValue(instances); + mainHandler.post(myRunnable); + } catch (Exception e) { + e.printStackTrace(); + } + }).start(); + } + +} diff --git a/app/src/main/res/layout/activity_manage_instances.xml b/app/src/main/res/layout/activity_manage_instances.xml index de115d9..fd142bd 100644 --- a/app/src/main/res/layout/activity_manage_instances.xml +++ b/app/src/main/res/layout/activity_manage_instances.xml @@ -18,6 +18,7 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/drawer_about_instance.xml b/app/src/main/res/layout/drawer_about_instance.xml new file mode 100644 index 0000000..116d4fb --- /dev/null +++ b/app/src/main/res/layout/drawer_about_instance.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/menu/instance_menu.xml b/app/src/main/res/menu/instance_menu.xml new file mode 100644 index 0000000..48d3f75 --- /dev/null +++ b/app/src/main/res/menu/instance_menu.xml @@ -0,0 +1,9 @@ + + + + diff --git a/app/src/main/res/menu/main_menu.xml b/app/src/main/res/menu/main_menu.xml index 619b9a4..735c085 100644 --- a/app/src/main/res/menu/main_menu.xml +++ b/app/src/main/res/menu/main_menu.xml @@ -9,11 +9,11 @@ android:title="@string/search" app:actionViewClass="androidx.appcompat.widget.SearchView" app:showAsAction="always|collapseActionView" /> -