mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-30 06:13:54 +01:00
Merge branch 'main' into main
This commit is contained in:
commit
50c900ea3f
27 changed files with 614 additions and 318 deletions
|
|
@ -5,6 +5,7 @@ import android.content.Context
|
|||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.webkit.ConsoleMessage
|
||||
import android.webkit.CookieManager
|
||||
import android.webkit.WebChromeClient
|
||||
import android.webkit.WebResourceRequest
|
||||
import android.webkit.WebView
|
||||
|
|
@ -28,13 +29,20 @@ import androidx.compose.runtime.remember
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.di.ApplicationlessInjection
|
||||
import fr.free.nrw.commons.wikidata.cookies.CommonsCookieJar
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* SingleWebViewActivity is a reusable activity webView based on a given url(initial url) and
|
||||
* closes itself when a specified success URL is reached to success url.
|
||||
*/
|
||||
class SingleWebViewActivity : ComponentActivity() {
|
||||
@Inject
|
||||
lateinit var cookieJar: CommonsCookieJar
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
|
@ -44,6 +52,11 @@ class SingleWebViewActivity : ComponentActivity() {
|
|||
finish()
|
||||
return
|
||||
}
|
||||
ApplicationlessInjection
|
||||
.getInstance(applicationContext)
|
||||
.commonsApplicationComponent
|
||||
.inject(this)
|
||||
setCookies(url)
|
||||
enableEdgeToEdge()
|
||||
setContent {
|
||||
Scaffold(
|
||||
|
|
@ -131,6 +144,7 @@ class SingleWebViewActivity : ComponentActivity() {
|
|||
|
||||
override fun onPageFinished(view: WebView?, url: String?) {
|
||||
super.onPageFinished(view, url)
|
||||
setCookies(url.orEmpty())
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -152,6 +166,20 @@ class SingleWebViewActivity : ComponentActivity() {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets cookies for the given URL using the cookies stored in the `CommonsCookieJar`.
|
||||
*
|
||||
* @param url The URL for which cookies need to be set.
|
||||
*/
|
||||
private fun setCookies(url: String) {
|
||||
CookieManager.getInstance().let {
|
||||
val cookies = cookieJar.loadForRequest(url.toHttpUrl())
|
||||
for (cookie in cookies) {
|
||||
it.setCookie(url, cookie.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val VANISH_ACCOUNT_URL = "VanishAccountUrl"
|
||||
private const val VANISH_ACCOUNT_SUCCESS_URL = "vanishAccountSuccessUrl"
|
||||
|
|
|
|||
|
|
@ -127,30 +127,64 @@ class CategoriesModel
|
|||
/**
|
||||
* Fetches details of every category associated with selected depictions, converts them into
|
||||
* CategoryItem and returns them in a list.
|
||||
* If a selected depiction has no categories, the categories in which its P18 belongs are
|
||||
* returned in the list.
|
||||
*
|
||||
* @param selectedDepictions selected DepictItems
|
||||
* @return List of CategoryItem associated with selected depictions
|
||||
*/
|
||||
private fun categoriesFromDepiction(selectedDepictions: List<DepictedItem>): Observable<MutableList<CategoryItem>>? =
|
||||
Observable
|
||||
.fromIterable(
|
||||
selectedDepictions.map { it.commonsCategories }.flatten(),
|
||||
).map { categoryItem ->
|
||||
categoryClient
|
||||
.getCategoriesByName(
|
||||
categoryItem.name,
|
||||
categoryItem.name,
|
||||
SEARCH_CATS_LIMIT,
|
||||
).map {
|
||||
CategoryItem(
|
||||
it[0].name,
|
||||
it[0].description,
|
||||
it[0].thumbnail,
|
||||
it[0].isSelected,
|
||||
)
|
||||
}.blockingGet()
|
||||
}.toList()
|
||||
.toObservable()
|
||||
private fun categoriesFromDepiction(selectedDepictions: List<DepictedItem>): Observable<MutableList<CategoryItem>>? {
|
||||
val observables = selectedDepictions.map { depictedItem ->
|
||||
if (depictedItem.commonsCategories.isEmpty()) {
|
||||
if (depictedItem.primaryImage == null) {
|
||||
return@map Observable.just(emptyList<CategoryItem>())
|
||||
}
|
||||
Observable.just(
|
||||
depictedItem.primaryImage
|
||||
).map { image ->
|
||||
categoryClient
|
||||
.getCategoriesOfImage(
|
||||
image,
|
||||
SEARCH_CATS_LIMIT,
|
||||
).map {
|
||||
it.map { category ->
|
||||
CategoryItem(
|
||||
category.name,
|
||||
category.description,
|
||||
category.thumbnail,
|
||||
category.isSelected,
|
||||
)
|
||||
}
|
||||
}.blockingGet()
|
||||
}.flatMapIterable { it }.toList()
|
||||
.toObservable()
|
||||
} else {
|
||||
Observable
|
||||
.fromIterable(
|
||||
depictedItem.commonsCategories,
|
||||
).map { categoryItem ->
|
||||
categoryClient
|
||||
.getCategoriesByName(
|
||||
categoryItem.name,
|
||||
categoryItem.name,
|
||||
SEARCH_CATS_LIMIT,
|
||||
).map {
|
||||
CategoryItem(
|
||||
it[0].name,
|
||||
it[0].description,
|
||||
it[0].thumbnail,
|
||||
it[0].isSelected,
|
||||
)
|
||||
}.blockingGet()
|
||||
}.toList()
|
||||
.toObservable()
|
||||
}
|
||||
}
|
||||
return Observable.concat(observables)
|
||||
.scan(mutableListOf<CategoryItem>()) { accumulator, currentList ->
|
||||
accumulator.apply { addAll(currentList) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches details of every category by their name, converts them into
|
||||
|
|
|
|||
|
|
@ -78,6 +78,24 @@ class CategoryClient
|
|||
),
|
||||
)
|
||||
|
||||
/**
|
||||
* Fetches categories belonging to an image (P18 of some wikidata entity).
|
||||
*
|
||||
* @param image P18 of some wikidata entity
|
||||
* @param itemLimit How many categories to return
|
||||
* @return Single Observable emitting the list of categories
|
||||
*/
|
||||
fun getCategoriesOfImage(
|
||||
image: String,
|
||||
itemLimit: Int,
|
||||
): Single<List<CategoryItem>> =
|
||||
responseMapper(
|
||||
categoryInterface.getCategoriesByTitles(
|
||||
"File:${image}",
|
||||
itemLimit,
|
||||
),
|
||||
)
|
||||
|
||||
/**
|
||||
* The method takes categoryName as input and returns a List of Subcategories
|
||||
* It uses the generator query API to get the subcategories in a category, 500 at a time.
|
||||
|
|
|
|||
|
|
@ -61,6 +61,21 @@ interface CategoryInterface {
|
|||
@Query("gacoffset") offset: Int,
|
||||
): Single<MwQueryResponse>
|
||||
|
||||
/**
|
||||
* Fetches non-hidden categories by titles.
|
||||
*
|
||||
* @param titles titles to fetch categories for (e.g. File:<P18 of a wikidata entity>)
|
||||
* @param itemLimit How many categories to return
|
||||
* @return MwQueryResponse
|
||||
*/
|
||||
@GET(
|
||||
"w/api.php?action=query&format=json&formatversion=2&generator=categories&prop=categoryinfo|description|pageimages&piprop=thumbnail&pithumbsize=70&gclshow=!hidden",
|
||||
)
|
||||
fun getCategoriesByTitles(
|
||||
@Query("titles") titles: String?,
|
||||
@Query("gcllimit") itemLimit: Int,
|
||||
): Single<MwQueryResponse>
|
||||
|
||||
@GET("w/api.php?action=query&format=json&formatversion=2&generator=categorymembers&gcmtype=subcat&prop=info&gcmlimit=50")
|
||||
fun getSubCategoryList(
|
||||
@Query("gcmtitle") categoryName: String,
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ data class Contribution constructor(
|
|||
*/
|
||||
fun formatCaptions(uploadMediaDetails: List<UploadMediaDetail>) =
|
||||
uploadMediaDetails
|
||||
.associate { it.languageCode!! to it.captionText!! }
|
||||
.associate { it.languageCode!! to it.captionText }
|
||||
.filter { it.value.isNotBlank() }
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -267,11 +267,11 @@ class DescriptionEditActivity :
|
|||
applicationContext,
|
||||
media,
|
||||
mediaDetail.languageCode!!,
|
||||
mediaDetail.captionText!!,
|
||||
mediaDetail.captionText,
|
||||
).subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { s: Boolean? ->
|
||||
updatedCaptions[mediaDetail.languageCode!!] = mediaDetail.captionText!!
|
||||
updatedCaptions[mediaDetail.languageCode!!] = mediaDetail.captionText
|
||||
media.captions = updatedCaptions
|
||||
Timber.d("Caption is added.")
|
||||
},
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import dagger.android.AndroidInjectionModule
|
|||
import dagger.android.AndroidInjector
|
||||
import dagger.android.support.AndroidSupportInjectionModule
|
||||
import fr.free.nrw.commons.CommonsApplication
|
||||
import fr.free.nrw.commons.activity.SingleWebViewActivity
|
||||
import fr.free.nrw.commons.auth.LoginActivity
|
||||
import fr.free.nrw.commons.contributions.ContributionsModule
|
||||
import fr.free.nrw.commons.explore.SearchModule
|
||||
|
|
@ -51,6 +52,8 @@ interface CommonsApplicationComponent : AndroidInjector<ApplicationlessInjection
|
|||
|
||||
fun inject(activity: LoginActivity)
|
||||
|
||||
fun inject(activity: SingleWebViewActivity)
|
||||
|
||||
fun inject(fragment: SettingsFragment)
|
||||
|
||||
fun inject(fragment: MoreBottomSheetFragment)
|
||||
|
|
|
|||
|
|
@ -467,18 +467,35 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the ContributionsFragment that is potentially the parent, grandparent, etc
|
||||
* fragment of this fragment.
|
||||
*
|
||||
* @return The ContributionsFragment instance. If the ContributionsFragment instance could not
|
||||
* be found, null is returned.
|
||||
*/
|
||||
private fun getContributionsFragmentParent(): ContributionsFragment? {
|
||||
var fragment: Fragment? = this
|
||||
|
||||
while (fragment != null && fragment !is ContributionsFragment) {
|
||||
fragment = fragment.parentFragment
|
||||
}
|
||||
|
||||
if (fragment == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
return fragment as ContributionsFragment
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (parentFragment != null && requireParentFragment().parentFragment != null) {
|
||||
// Added a check because, not necessarily, the parent fragment
|
||||
// will have a parent fragment, say in the case when MediaDetailPagerFragment
|
||||
// is directly started by the CategoryImagesActivity
|
||||
if (parentFragment is ContributionsFragment) {
|
||||
(((parentFragment as ContributionsFragment)
|
||||
.parentFragment) as ContributionsFragment).binding.cardViewNearby.visibility =
|
||||
View.GONE
|
||||
}
|
||||
|
||||
val contributionsFragment: ContributionsFragment? = this.getContributionsFragmentParent()
|
||||
if (contributionsFragment?.binding != null) {
|
||||
contributionsFragment.binding.cardViewNearby.visibility = View.GONE
|
||||
}
|
||||
|
||||
// detail provider is null when fragment is shown in review activity
|
||||
media = if (detailProvider != null) {
|
||||
detailProvider!!.getMediaAtPosition(index)
|
||||
|
|
@ -1569,7 +1586,7 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
|
|||
mediaDetail: UploadMediaDetail,
|
||||
updatedCaptions: MutableMap<String, String>
|
||||
) {
|
||||
updatedCaptions[mediaDetail.languageCode!!] = mediaDetail.captionText!!
|
||||
updatedCaptions[mediaDetail.languageCode!!] = mediaDetail.captionText
|
||||
media!!.captions = updatedCaptions
|
||||
}
|
||||
|
||||
|
|
@ -1737,10 +1754,11 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
|
|||
return
|
||||
}
|
||||
ProfileActivity.startYourself(
|
||||
activity,
|
||||
media!!.user,
|
||||
sessionManager.userName != media!!.user
|
||||
requireActivity(), // Ensure this is a non-null Activity context
|
||||
media?.user ?: "", // Provide a fallback value if media?.user is null
|
||||
sessionManager.userName != media?.user // This can remain as is, null check will apply
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,265 +0,0 @@
|
|||
package fr.free.nrw.commons.profile;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.FileProvider;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import fr.free.nrw.commons.R;
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.ViewPagerAdapter;
|
||||
import fr.free.nrw.commons.auth.SessionManager;
|
||||
import fr.free.nrw.commons.contributions.ContributionsFragment;
|
||||
import fr.free.nrw.commons.databinding.ActivityProfileBinding;
|
||||
import fr.free.nrw.commons.profile.achievements.AchievementsFragment;
|
||||
import fr.free.nrw.commons.profile.leaderboard.LeaderboardFragment;
|
||||
import fr.free.nrw.commons.theme.BaseActivity;
|
||||
import fr.free.nrw.commons.utils.DialogUtil;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* This activity will set two tabs, achievements and
|
||||
* each tab will have their own fragments
|
||||
*/
|
||||
public class ProfileActivity extends BaseActivity {
|
||||
|
||||
private FragmentManager supportFragmentManager;
|
||||
|
||||
public ActivityProfileBinding binding;
|
||||
|
||||
@Inject
|
||||
SessionManager sessionManager;
|
||||
|
||||
private ViewPagerAdapter viewPagerAdapter;
|
||||
private AchievementsFragment achievementsFragment;
|
||||
private LeaderboardFragment leaderboardFragment;
|
||||
|
||||
public static final String KEY_USERNAME ="username";
|
||||
public static final String KEY_SHOULD_SHOW_CONTRIBUTIONS ="shouldShowContributions";
|
||||
|
||||
String userName;
|
||||
private boolean shouldShowContributions;
|
||||
|
||||
ContributionsFragment contributionsFragment;
|
||||
|
||||
public void setScroll(boolean canScroll){
|
||||
binding.viewPager.setCanScroll(canScroll);
|
||||
}
|
||||
@Override
|
||||
protected void onRestoreInstanceState(final Bundle savedInstanceState) {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
if (savedInstanceState != null) {
|
||||
userName = savedInstanceState.getString(KEY_USERNAME);
|
||||
shouldShowContributions = savedInstanceState.getBoolean(KEY_SHOULD_SHOW_CONTRIBUTIONS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
binding = ActivityProfileBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
setSupportActionBar(binding.toolbarBinding.toolbar);
|
||||
|
||||
|
||||
binding.toolbarBinding.toolbar.setNavigationOnClickListener(view -> {
|
||||
onSupportNavigateUp();
|
||||
});
|
||||
|
||||
userName = getIntent().getStringExtra(KEY_USERNAME);
|
||||
setTitle(userName);
|
||||
shouldShowContributions = getIntent().getBooleanExtra(KEY_SHOULD_SHOW_CONTRIBUTIONS, false);
|
||||
|
||||
supportFragmentManager = getSupportFragmentManager();
|
||||
viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager());
|
||||
binding.viewPager.setAdapter(viewPagerAdapter);
|
||||
binding.tabLayout.setupWithViewPager(binding.viewPager);
|
||||
setTabs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate up event
|
||||
* @return boolean
|
||||
*/
|
||||
@Override
|
||||
public boolean onSupportNavigateUp() {
|
||||
onBackPressed();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a way to change current activity to AchievementActivity
|
||||
*
|
||||
* @param context
|
||||
*/
|
||||
public static void startYourself(final Context context, final String userName,
|
||||
final boolean shouldShowContributions) {
|
||||
Intent intent = new Intent(context, ProfileActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
intent.putExtra(KEY_USERNAME, userName);
|
||||
intent.putExtra(KEY_SHOULD_SHOW_CONTRIBUTIONS, shouldShowContributions);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the tabs for the fragments
|
||||
*/
|
||||
private void setTabs() {
|
||||
List<Fragment> fragmentList = new ArrayList<>();
|
||||
List<String> titleList = new ArrayList<>();
|
||||
achievementsFragment = new AchievementsFragment();
|
||||
Bundle achievementsBundle = new Bundle();
|
||||
achievementsBundle.putString(KEY_USERNAME, userName);
|
||||
achievementsFragment.setArguments(achievementsBundle);
|
||||
fragmentList.add(achievementsFragment);
|
||||
|
||||
titleList.add(getResources().getString(R.string.achievements_tab_title).toUpperCase());
|
||||
leaderboardFragment = new LeaderboardFragment();
|
||||
Bundle leaderBoardBundle = new Bundle();
|
||||
leaderBoardBundle.putString(KEY_USERNAME, userName);
|
||||
leaderboardFragment.setArguments(leaderBoardBundle);
|
||||
|
||||
fragmentList.add(leaderboardFragment);
|
||||
titleList.add(getResources().getString(R.string.leaderboard_tab_title).toUpperCase(Locale.ROOT));
|
||||
|
||||
contributionsFragment = new ContributionsFragment();
|
||||
Bundle contributionsListBundle = new Bundle();
|
||||
contributionsListBundle.putString(KEY_USERNAME, userName);
|
||||
contributionsFragment.setArguments(contributionsListBundle);
|
||||
fragmentList.add(contributionsFragment);
|
||||
titleList.add(getString(R.string.contributions_fragment).toUpperCase(Locale.ROOT));
|
||||
|
||||
viewPagerAdapter.setTabData(fragmentList, titleList);
|
||||
viewPagerAdapter.notifyDataSetChanged();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
getCompositeDisposable().clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* To inflate menu
|
||||
* @param menu Menu
|
||||
* @return boolean
|
||||
*/
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(final Menu menu) {
|
||||
final MenuInflater menuInflater = getMenuInflater();
|
||||
menuInflater.inflate(R.menu.menu_about, menu);
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
/**
|
||||
* To receive the id of selected item and handle further logic for that selected item
|
||||
* @param item MenuItem
|
||||
* @return boolean
|
||||
*/
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
// take screenshot in form of bitmap and show it in Alert Dialog
|
||||
if (item.getItemId() == R.id.share_app_icon) {
|
||||
final View rootView = getWindow().getDecorView().findViewById(android.R.id.content);
|
||||
final Bitmap screenShot = Utils.getScreenShot(rootView);
|
||||
showAlert(screenShot);
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
/**
|
||||
* It displays the alertDialog with Image of screenshot
|
||||
* @param screenshot screenshot of the present screen
|
||||
*/
|
||||
public void showAlert(final Bitmap screenshot) {
|
||||
final LayoutInflater factory = LayoutInflater.from(this);
|
||||
final View view = factory.inflate(R.layout.image_alert_layout, null);
|
||||
final ImageView screenShotImage = view.findViewById(R.id.alert_image);
|
||||
screenShotImage.setImageBitmap(screenshot);
|
||||
final TextView shareMessage = view.findViewById(R.id.alert_text);
|
||||
shareMessage.setText(R.string.achievements_share_message);
|
||||
DialogUtil.showAlertDialog(this,
|
||||
null,
|
||||
null,
|
||||
getString(R.string.about_translate_proceed),
|
||||
getString(R.string.cancel),
|
||||
() -> shareScreen(screenshot),
|
||||
() -> {},
|
||||
view
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* To take bitmap and store it temporary storage and share it
|
||||
* @param bitmap bitmap of screenshot
|
||||
*/
|
||||
void shareScreen(final Bitmap bitmap) {
|
||||
try {
|
||||
final File file = new File(getExternalCacheDir(), "screen.png");
|
||||
final FileOutputStream fileOutputStream = new FileOutputStream(file);
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fileOutputStream);
|
||||
fileOutputStream.flush();
|
||||
fileOutputStream.close();
|
||||
file.setReadable(true, false);
|
||||
|
||||
final Uri fileUri = FileProvider
|
||||
.getUriForFile(getApplicationContext(),
|
||||
getPackageName() + ".provider", file);
|
||||
grantUriPermission(getPackageName(), fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||
final Intent intent = new Intent(android.content.Intent.ACTION_SEND);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.putExtra(Intent.EXTRA_STREAM, fileUri);
|
||||
intent.setType("image/png");
|
||||
startActivity(Intent.createChooser(intent, getString(R.string.share_image_via)));
|
||||
} catch (final IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(@NonNull final Bundle outState) {
|
||||
outState.putString(KEY_USERNAME, userName);
|
||||
outState.putBoolean(KEY_SHOULD_SHOW_CONTRIBUTIONS, shouldShowContributions);
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
// Checking if MediaDetailPagerFragment is visible, If visible then show ContributionListFragment else close the ProfileActivity
|
||||
if(contributionsFragment != null && contributionsFragment.getMediaDetailPagerFragment() != null && contributionsFragment.getMediaDetailPagerFragment().isVisible()) {
|
||||
contributionsFragment.backButtonClicked();
|
||||
binding.tabLayout.setVisibility(View.VISIBLE);
|
||||
}else {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To set the visibility of tab layout
|
||||
* @param isVisible boolean
|
||||
*/
|
||||
public void setTabLayoutVisibility(boolean isVisible) {
|
||||
binding.tabLayout.setVisibility(isVisible ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
229
app/src/main/java/fr/free/nrw/commons/profile/ProfileActivity.kt
Normal file
229
app/src/main/java/fr/free/nrw/commons/profile/ProfileActivity.kt
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
package fr.free.nrw.commons.profile
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.*
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.fragment.app.Fragment
|
||||
import fr.free.nrw.commons.R
|
||||
import fr.free.nrw.commons.Utils
|
||||
import fr.free.nrw.commons.ViewPagerAdapter
|
||||
import fr.free.nrw.commons.auth.SessionManager
|
||||
import fr.free.nrw.commons.contributions.ContributionsFragment
|
||||
import fr.free.nrw.commons.databinding.ActivityProfileBinding
|
||||
import fr.free.nrw.commons.profile.achievements.AchievementsFragment
|
||||
import fr.free.nrw.commons.profile.leaderboard.LeaderboardFragment
|
||||
import fr.free.nrw.commons.theme.BaseActivity
|
||||
import fr.free.nrw.commons.utils.DialogUtil
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* This activity will set two tabs, achievements and
|
||||
* each tab will have their own fragments
|
||||
*/
|
||||
class ProfileActivity : BaseActivity() {
|
||||
|
||||
lateinit var binding: ActivityProfileBinding
|
||||
|
||||
@Inject
|
||||
lateinit var sessionManager: SessionManager
|
||||
|
||||
private lateinit var viewPagerAdapter: ViewPagerAdapter
|
||||
private lateinit var achievementsFragment: AchievementsFragment
|
||||
private lateinit var leaderboardFragment: LeaderboardFragment
|
||||
private lateinit var userName: String
|
||||
private var shouldShowContributions: Boolean = false
|
||||
private var contributionsFragment: ContributionsFragment? = null
|
||||
|
||||
fun setScroll(canScroll: Boolean) {
|
||||
binding.viewPager.setCanScroll(canScroll)
|
||||
}
|
||||
|
||||
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
|
||||
super.onRestoreInstanceState(savedInstanceState)
|
||||
savedInstanceState.let {
|
||||
userName = it.getString(KEY_USERNAME, "")
|
||||
shouldShowContributions = it.getBoolean(KEY_SHOULD_SHOW_CONTRIBUTIONS)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
binding = ActivityProfileBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
setSupportActionBar(binding.toolbarBinding.toolbar)
|
||||
|
||||
binding.toolbarBinding.toolbar.setNavigationOnClickListener {
|
||||
onSupportNavigateUp()
|
||||
}
|
||||
|
||||
userName = intent.getStringExtra(KEY_USERNAME) ?: ""
|
||||
title = userName
|
||||
shouldShowContributions = intent.getBooleanExtra(KEY_SHOULD_SHOW_CONTRIBUTIONS, false)
|
||||
|
||||
viewPagerAdapter = ViewPagerAdapter(supportFragmentManager)
|
||||
binding.viewPager.adapter = viewPagerAdapter
|
||||
binding.tabLayout.setupWithViewPager(binding.viewPager)
|
||||
setTabs()
|
||||
}
|
||||
|
||||
override fun onSupportNavigateUp(): Boolean {
|
||||
onBackPressed()
|
||||
return true
|
||||
}
|
||||
|
||||
private fun setTabs() {
|
||||
val fragmentList = mutableListOf<Fragment>()
|
||||
val titleList = mutableListOf<String>()
|
||||
|
||||
// Add Achievements tab
|
||||
achievementsFragment = AchievementsFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putString(KEY_USERNAME, userName)
|
||||
}
|
||||
}
|
||||
fragmentList.add(achievementsFragment)
|
||||
titleList.add(resources.getString(R.string.achievements_tab_title).uppercase())
|
||||
|
||||
// Add Leaderboard tab
|
||||
leaderboardFragment = LeaderboardFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putString(KEY_USERNAME, userName)
|
||||
}
|
||||
}
|
||||
fragmentList.add(leaderboardFragment)
|
||||
titleList.add(resources.getString(R.string.leaderboard_tab_title).uppercase(Locale.ROOT))
|
||||
|
||||
// Add Contributions tab
|
||||
contributionsFragment = ContributionsFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putString(KEY_USERNAME, userName)
|
||||
}
|
||||
}
|
||||
contributionsFragment?.let {
|
||||
fragmentList.add(it)
|
||||
titleList.add(getString(R.string.contributions_fragment).uppercase(Locale.ROOT))
|
||||
}
|
||||
|
||||
viewPagerAdapter.setTabData(fragmentList, titleList)
|
||||
viewPagerAdapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
public override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
compositeDisposable.clear()
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_about, menu)
|
||||
return super.onCreateOptionsMenu(menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
return when (item.itemId) {
|
||||
R.id.share_app_icon -> {
|
||||
val rootView = window.decorView.findViewById<View>(android.R.id.content)
|
||||
val screenShot = Utils.getScreenShot(rootView)
|
||||
if (screenShot == null) {
|
||||
Log.e("ERROR", "ScreenShot is null")
|
||||
return false
|
||||
}
|
||||
showAlert(screenShot)
|
||||
true
|
||||
}
|
||||
else -> super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
fun showAlert(screenshot: Bitmap) {
|
||||
val view = layoutInflater.inflate(R.layout.image_alert_layout, null)
|
||||
val screenShotImage = view.findViewById<ImageView>(R.id.alert_image)
|
||||
val shareMessage = view.findViewById<TextView>(R.id.alert_text)
|
||||
|
||||
screenShotImage.setImageBitmap(screenshot)
|
||||
shareMessage.setText(R.string.achievements_share_message)
|
||||
|
||||
DialogUtil.showAlertDialog(
|
||||
this,
|
||||
null,
|
||||
null,
|
||||
getString(R.string.about_translate_proceed),
|
||||
getString(R.string.cancel),
|
||||
{ shareScreen(screenshot) },
|
||||
{},
|
||||
view
|
||||
)
|
||||
}
|
||||
|
||||
private fun shareScreen(bitmap: Bitmap) {
|
||||
try {
|
||||
val file = File(externalCacheDir, "screen.png")
|
||||
FileOutputStream(file).use { out ->
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, out)
|
||||
out.flush()
|
||||
}
|
||||
file.setReadable(true, false)
|
||||
|
||||
val fileUri = FileProvider.getUriForFile(
|
||||
applicationContext,
|
||||
"$packageName.provider",
|
||||
file
|
||||
)
|
||||
|
||||
grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
|
||||
val intent = Intent(Intent.ACTION_SEND).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
putExtra(Intent.EXTRA_STREAM, fileUri)
|
||||
type = "image/png"
|
||||
}
|
||||
startActivity(Intent.createChooser(intent, getString(R.string.share_image_via)))
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putString(KEY_USERNAME, userName)
|
||||
outState.putBoolean(KEY_SHOULD_SHOW_CONTRIBUTIONS, shouldShowContributions)
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (contributionsFragment?.mediaDetailPagerFragment?.isVisible == true) {
|
||||
contributionsFragment?.backButtonClicked()
|
||||
binding.tabLayout.visibility = View.VISIBLE
|
||||
} else {
|
||||
super.onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
fun setTabLayoutVisibility(isVisible: Boolean) {
|
||||
binding.tabLayout.visibility = if (isVisible) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val KEY_USERNAME = "username"
|
||||
const val KEY_SHOULD_SHOW_CONTRIBUTIONS = "shouldShowContributions"
|
||||
|
||||
@JvmStatic
|
||||
fun startYourself(context: Context, userName: String, shouldShowContributions: Boolean) {
|
||||
val intent = Intent(context, ProfileActivity::class.java).apply {
|
||||
addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT or Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
||||
putExtra(KEY_USERNAME, userName)
|
||||
putExtra(KEY_SHOULD_SHOW_CONTRIBUTIONS, shouldShowContributions)
|
||||
}
|
||||
context.startActivity(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -58,7 +58,7 @@ class LeaderboardListAdapter : PagedListAdapter<LeaderboardList, ListViewHolder>
|
|||
if (view.context is ProfileActivity) {
|
||||
((view.context) as Activity).finish()
|
||||
}
|
||||
ProfileActivity.startYourself(view.context, item.username, true)
|
||||
ProfileActivity.startYourself(view.context, item.username?:"", true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,14 +23,14 @@ data class UploadMediaDetail(
|
|||
* The caption text for the item being uploaded.
|
||||
* @param captionText The caption text.
|
||||
*/
|
||||
var captionText: String? = "",
|
||||
var captionText: String = "",
|
||||
) : Parcelable {
|
||||
fun javaCopy() = copy()
|
||||
|
||||
constructor(place: Place?) : this(
|
||||
place?.language,
|
||||
place?.longDescription,
|
||||
place?.name,
|
||||
place?.name ?: "",
|
||||
)
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ class CategoriesPresenter
|
|||
*/
|
||||
private fun getImageTitleList(): List<String> =
|
||||
repository.getUploads()
|
||||
.map { it.uploadMediaDetails[0].captionText!! }
|
||||
.map { it.uploadMediaDetails[0].captionText }
|
||||
.filterNot { TextUtils.isEmpty(it) }
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -68,6 +68,9 @@ data class DepictedItem constructor(
|
|||
entity.id(),
|
||||
)
|
||||
|
||||
val primaryImage: String?
|
||||
get() = imageUrl?.split('-')?.lastOrNull()
|
||||
|
||||
override fun equals(other: Any?) =
|
||||
when {
|
||||
this === other -> true
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue