diff --git a/app/src/main/java/fr/free/nrw/commons/Utils.java b/app/src/main/java/fr/free/nrw/commons/Utils.java index 587a65467..232fa6605 100644 --- a/app/src/main/java/fr/free/nrw/commons/Utils.java +++ b/app/src/main/java/fr/free/nrw/commons/Utils.java @@ -178,9 +178,13 @@ public class Utils { public static Bitmap getScreenShot(View view) { View screenView = view.getRootView(); screenView.setDrawingCacheEnabled(true); - Bitmap bitmap = Bitmap.createBitmap(screenView.getDrawingCache()); - screenView.setDrawingCacheEnabled(false); - return bitmap; + Bitmap drawingCache = screenView.getDrawingCache(); + if (drawingCache != null) { + Bitmap bitmap = Bitmap.createBitmap(drawingCache); + screenView.setDrawingCacheEnabled(false); + return bitmap; + } + return null; } /* diff --git a/app/src/main/java/fr/free/nrw/commons/profile/ProfileActivity.java b/app/src/main/java/fr/free/nrw/commons/profile/ProfileActivity.java index b443efa42..2e0f6c261 100644 --- a/app/src/main/java/fr/free/nrw/commons/profile/ProfileActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/profile/ProfileActivity.java @@ -1,8 +1,20 @@ package fr.free.nrw.commons.profile; +import android.app.AlertDialog; 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.appcompat.widget.Toolbar; +import androidx.core.content.FileProvider; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.viewpager.widget.ViewPager; @@ -10,12 +22,18 @@ import butterknife.BindView; import butterknife.ButterKnife; import com.google.android.material.tabs.TabLayout; 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.profile.achievements.AchievementsFragment; import fr.free.nrw.commons.profile.leaderboard.LeaderboardFragment; import fr.free.nrw.commons.theme.BaseActivity; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; import java.util.ArrayList; import java.util.List; +import javax.inject.Inject; /** * This activity will set two tabs, achievements and @@ -31,6 +49,12 @@ public class ProfileActivity extends BaseActivity { @BindView(R.id.tab_layout) TabLayout tabLayout; + @BindView(R.id.toolbar) + Toolbar toolbar; + + @Inject + SessionManager sessionManager; + private ViewPagerAdapter viewPagerAdapter; private AchievementsFragment achievementsFragment; private LeaderboardFragment leaderboardFragment; @@ -40,7 +64,9 @@ public class ProfileActivity extends BaseActivity { super.onCreate(savedInstanceState); setContentView(R.layout.activity_profile); ButterKnife.bind(this); - setTitle(R.string.Profile); + setSupportActionBar(toolbar); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + setTitle(sessionManager.getUserName()); supportFragmentManager = getSupportFragmentManager(); viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager()); @@ -49,6 +75,16 @@ public class ProfileActivity extends BaseActivity { setTabs(); } + /** + * Navigate up event + * @return boolean + */ + @Override + public boolean onSupportNavigateUp() { + onBackPressed(); + return true; + } + /** * Creates a way to change current activity to AchievementActivity * @param context @@ -75,10 +111,84 @@ public class ProfileActivity extends BaseActivity { viewPagerAdapter.notifyDataSetChanged(); } + @Override public void onDestroy() { super.onDestroy(); compositeDisposable.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 AlertDialog.Builder alert = new AlertDialog.Builder(this); + 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); + alert.setView(view); + alert.setPositiveButton(R.string.about_translate_proceed, (dialog, which) -> shareScreen(screenshot)); + alert.setNegativeButton(android.R.string.cancel, (dialog, which) -> dialog.cancel()); + alert.show(); + } + + /** + * 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(); + } + } } \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/profile/achievements/AchievementsFragment.java b/app/src/main/java/fr/free/nrw/commons/profile/achievements/AchievementsFragment.java index efd416631..fd2864398 100644 --- a/app/src/main/java/fr/free/nrw/commons/profile/achievements/AchievementsFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/profile/achievements/AchievementsFragment.java @@ -3,14 +3,10 @@ package fr.free.nrw.commons.profile.achievements; import android.accounts.Account; import android.app.AlertDialog; import android.app.AlertDialog.Builder; -import android.content.Intent; -import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; import android.util.DisplayMetrics; import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; @@ -21,22 +17,11 @@ import android.widget.RelativeLayout; import android.widget.TextView; import androidx.appcompat.view.ContextThemeWrapper; import androidx.constraintlayout.widget.ConstraintLayout; -import androidx.core.content.FileProvider; import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat; - -import com.dinuscxj.progressbar.CircleProgressBar; -import org.apache.commons.lang3.StringUtils; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.Objects; - -import javax.inject.Inject; - import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; +import com.dinuscxj.progressbar.CircleProgressBar; import fr.free.nrw.commons.R; import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.auth.SessionManager; @@ -46,6 +31,9 @@ import fr.free.nrw.commons.utils.ViewUtil; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.schedulers.Schedulers; +import java.util.Objects; +import javax.inject.Inject; +import org.apache.commons.lang3.StringUtils; import timber.log.Timber; /** @@ -174,74 +162,6 @@ public class AchievementsFragment extends CommonsDaggerSupportFragment { return rootView; } - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) { - // Inflate the menu; this adds items to the action bar if it is present. - super.onCreateOptionsMenu(menu, menuInflater); - menuInflater.inflate(R.menu.menu_about, menu); - item = menu.getItem(0); - item.setVisible(false); - } - - /** - * To receive the id of selected item and handle further logic for that selected item - */ - @Override - public boolean onOptionsItemSelected(MenuItem item) { - int id = item.getItemId(); - // take screenshot in form of bitmap and show it in Alert Dialog - if (id == R.id.share_app_icon) { - View rootView = getActivity().getWindow().getDecorView().findViewById(android.R.id.content); - Bitmap screenShot = Utils.getScreenShot(rootView); - showAlert(screenShot); - } - - return super.onOptionsItemSelected(item); - } - - /** - * It displays the alertDialog with Image of screenshot - * @param screenshot - */ - public void showAlert(Bitmap screenshot){ - AlertDialog.Builder alertadd = new AlertDialog.Builder(getActivity()); - LayoutInflater factory = LayoutInflater.from(getActivity()); - final View view = factory.inflate(R.layout.image_alert_layout, null); - ImageView screenShotImage = view.findViewById(R.id.alert_image); - screenShotImage.setImageBitmap(screenshot); - TextView shareMessage = view.findViewById(R.id.alert_text); - shareMessage.setText(R.string.achievements_share_message); - alertadd.setView(view); - alertadd.setPositiveButton(R.string.about_translate_proceed, (dialog, which) -> shareScreen(screenshot)); - alertadd.setNegativeButton(android.R.string.cancel, (dialog, which) -> dialog.cancel()); - alertadd.show(); - } - - /** - * To take bitmap and store it temporary storage and share it - * @param bitmap - */ - void shareScreen(Bitmap bitmap) { - try { - File file = new File(getActivity().getExternalCacheDir(), "screen.png"); - FileOutputStream fOut = new FileOutputStream(file); - bitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut); - fOut.flush(); - fOut.close(); - file.setReadable(true, false); - Uri fileUri = FileProvider - .getUriForFile(getActivity().getApplicationContext(), getActivity().getPackageName()+".provider", file); - getActivity().grantUriPermission(getActivity().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 (IOException e) { - e.printStackTrace(); - } - } - /** * To invoke the AlertDialog on clicking info button */ diff --git a/app/src/test/kotlin/fr/free/nrw/commons/profile/ProfileActivityTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/profile/ProfileActivityTest.kt index 1fe0d969e..2671f69f6 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/profile/ProfileActivityTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/profile/ProfileActivityTest.kt @@ -1,30 +1,46 @@ package fr.free.nrw.commons.profile import android.content.Context +import android.graphics.Bitmap +import android.os.Looper +import android.view.Menu +import android.view.MenuItem +import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication import org.junit.Assert import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.powermock.api.mockito.PowerMockito +import org.mockito.Mock +import org.mockito.MockitoAnnotations import org.robolectric.Robolectric import org.robolectric.RobolectricTestRunner +import org.robolectric.RuntimeEnvironment +import org.robolectric.Shadows import org.robolectric.annotation.Config +import org.robolectric.fakes.RoboMenu +import org.robolectric.fakes.RoboMenuItem +import java.lang.reflect.Method @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) class ProfileActivityTest { + @Mock private lateinit var activity: ProfileActivity - private lateinit var profileActivity: ProfileActivity + + @Mock private lateinit var mockContext: Context + @Mock + private lateinit var bitmap: Bitmap + @Before fun setUp() { + MockitoAnnotations.initMocks(this) activity = Robolectric.buildActivity(ProfileActivity::class.java).create().get() - mockContext = PowerMockito.mock(Context::class.java) - profileActivity = PowerMockito.mock(ProfileActivity::class.java) + mockContext = RuntimeEnvironment.application.applicationContext } @Test @@ -41,8 +57,53 @@ class ProfileActivityTest { @Test @Throws(Exception::class) - fun testStartYourself() { - ProfileActivity.startYourself(mockContext) + fun testOnCreateOptionsMenu() { + val menu: Menu = RoboMenu(mockContext) + activity.onCreateOptionsMenu(menu) } + @Test + @Throws(Exception::class) + fun testOnOptionsItemSelected() { + val menuItem: MenuItem = RoboMenuItem(R.menu.menu_about) + Shadows.shadowOf(Looper.getMainLooper()).idle() + activity.onOptionsItemSelected(menuItem) + } + + @Test + @Throws(Exception::class) + fun testOnOptionsShareItemSelected() { + val menuItemShare: MenuItem = RoboMenuItem(R.id.share_app_icon) + activity.onOptionsItemSelected(menuItemShare) + } + + @Test + @Throws(Exception::class) + fun testStartYourself() { + ProfileActivity.startYourself(activity) + } + + @Test + @Throws(Exception::class) + fun testShowAlert() { + Shadows.shadowOf(Looper.getMainLooper()).idle() + activity.showAlert(bitmap) + } + + @Test + @Throws(Exception::class) + fun testShareScreen() { + Shadows.shadowOf(Looper.getMainLooper()).idle() + val method: Method = ProfileActivity::class.java.getDeclaredMethod( + "shareScreen", Bitmap::class.java + ) + method.isAccessible = true + method.invoke(activity, bitmap) + } + + @Test + @Throws(Exception::class) + fun testOnSupportNavigateUp() { + activity.onSupportNavigateUp() + } } \ No newline at end of file diff --git a/app/src/test/kotlin/fr/free/nrw/commons/profile/achievements/AchievementsFragmentUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/profile/achievements/AchievementsFragmentUnitTests.kt index 306c50490..bf3e576db 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/profile/achievements/AchievementsFragmentUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/profile/achievements/AchievementsFragmentUnitTests.kt @@ -1,7 +1,6 @@ package fr.free.nrw.commons.profile.achievements import android.content.Context -import android.graphics.Bitmap import android.os.Looper import android.view.MenuItem import android.widget.ImageView @@ -83,9 +82,6 @@ class AchievementsFragmentUnitTests { @Mock private lateinit var imageUploadedText: TextView - @Mock - private lateinit var bitmap: Bitmap - @Mock private lateinit var progressBar: ProgressBar @@ -143,14 +139,6 @@ class AchievementsFragmentUnitTests { Assert.assertNotNull(fragment) } - @Test - @Throws(Exception::class) - fun testShowAlert() { - Shadows.shadowOf(Looper.getMainLooper()).idle() - fragment.showAlert(bitmap) - } - - @Test @Throws(Exception::class) fun testShowInfoDialog() { @@ -329,16 +317,4 @@ class AchievementsFragmentUnitTests { method.isAccessible = true method.invoke(fragment) } - - @Test - @Throws(Exception::class) - fun testShareScreen() { - Shadows.shadowOf(Looper.getMainLooper()).idle() - val method: Method = AchievementsFragment::class.java.getDeclaredMethod( - "shareScreen", Bitmap::class.java - ) - method.isAccessible = true - method.invoke(fragment, bitmap) - } - } \ No newline at end of file