Add reporting and link sharing functionality, polish UI

This commit is contained in:
Matthieu 2020-10-04 23:27:06 +02:00
parent a769d404a6
commit 50dd0bad51
65 changed files with 1021 additions and 606 deletions

View File

@ -65,7 +65,7 @@ dependencies {
* AndroidX dependencies: * AndroidX dependencies:
*/ */
implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.core:core-ktx:1.3.1' implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.preference:preference:1.1.1' implementation 'androidx.preference:preference:1.1.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
implementation 'androidx.navigation:navigation-fragment:2.3.0' implementation 'androidx.navigation:navigation-fragment:2.3.0'
@ -80,6 +80,7 @@ dependencies {
implementation 'androidx.lifecycle:lifecycle-viewmodel-savedstate:2.2.0' implementation 'androidx.lifecycle:lifecycle-viewmodel-savedstate:2.2.0'
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0" implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.2.0"
implementation "androidx.annotation:annotation:1.1.0" implementation "androidx.annotation:annotation:1.1.0"
implementation 'androidx.gridlayout:gridlayout:1.0.0'
// Use the most recent version of CameraX // Use the most recent version of CameraX
def camerax_version = '1.0.0-beta08' def camerax_version = '1.0.0-beta08'
@ -104,8 +105,6 @@ dependencies {
implementation 'com.google.android.material:material:1.2.1' implementation 'com.google.android.material:material:1.2.1'
implementation 'com.google.android:flexbox:2.0.1'
//Dagger (dependency injection) //Dagger (dependency injection)
implementation 'com.google.dagger:dagger-android:2.28.3' implementation 'com.google.dagger:dagger-android:2.28.3'
implementation 'com.google.dagger:dagger-android-support:2.28.3' implementation 'com.google.dagger:dagger-android-support:2.28.3'
@ -138,13 +137,14 @@ dependencies {
implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation "com.mikepenz:materialdrawer:8.1.4" implementation "com.mikepenz:materialdrawer:8.1.5"
// Add for NavController support // Add for NavController support
implementation "com.mikepenz:materialdrawer-nav:8.0.3" implementation "com.mikepenz:materialdrawer-nav:8.1.5"
//iconics //iconics
implementation "com.mikepenz:materialdrawer-iconics:8.1.4" implementation "com.mikepenz:iconics-core:5.0.3"
implementation "com.mikepenz:iconics-views:5.0.2" implementation "com.mikepenz:materialdrawer-iconics:8.1.5"
implementation "com.mikepenz:iconics-views:5.0.3"
implementation 'com.mikepenz:google-material-typeface:3.0.1.4.original-kotlin@aar' implementation 'com.mikepenz:google-material-typeface:3.0.1.4.original-kotlin@aar'

View File

@ -24,9 +24,12 @@
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
tools:replace="android:allowBackup"> tools:replace="android:allowBackup">
<activity android:name=".CameraActivity"></activity>
<activity <activity
android:name=".PhotoEditActivity" android:name=".ReportActivity"
android:theme="@style/AppTheme.NoActionBar" /> android:screenOrientation="sensorPortrait"
tools:ignore="LockedOrientationActivity" />
<activity android:name=".PhotoEditActivity" />
<activity <activity
android:name=".PostCreationActivity" android:name=".PostCreationActivity"
android:screenOrientation="sensorPortrait" android:screenOrientation="sensorPortrait"
@ -47,19 +50,24 @@
<activity <activity
android:name=".SettingsActivity" android:name=".SettingsActivity"
android:label="@string/title_activity_settings2" android:label="@string/title_activity_settings2"
android:parentActivityName=".MainActivity"
android:screenOrientation="sensorPortrait" android:screenOrientation="sensorPortrait"
tools:ignore="LockedOrientationActivity" tools:ignore="LockedOrientationActivity" />
android:parentActivityName=".MainActivity" />
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:screenOrientation="sensorPortrait" android:screenOrientation="sensorPortrait"
android:theme="@style/AppTheme.Launcher" android:theme="@style/AppTheme.Launcher"
android:windowSoftInputMode="adjustPan"
tools:ignore="LockedOrientationActivity"> tools:ignore="LockedOrientationActivity">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
<meta-data
android:name="android.app.default_searchable"
android:value=".SearchActivity" />
</activity> </activity>
<activity <activity
android:name=".LoginActivity" android:name=".LoginActivity"
@ -89,28 +97,23 @@
android:screenOrientation="sensorPortrait" android:screenOrientation="sensorPortrait"
tools:ignore="LockedOrientationActivity"> tools:ignore="LockedOrientationActivity">
<intent-filter> <intent-filter>
<action <action android:name="android.intent.action.SEARCH" />
android:name="android.intent.action.SEARCH"
android:screenOrientation="sensorPortrait"
tools:ignore="LockedOrientationActivity" />
</intent-filter> </intent-filter>
<meta-data <meta-data
android:name="android.app.searchable" android:name="android.app.searchable"
android:resource="@xml/searchable" /> android:resource="@xml/searchable" />
</activity> </activity>
<activity <activity
android:name=".AboutActivity" android:name=".AboutActivity"
android:parentActivityName=".SettingsActivity"
android:screenOrientation="sensorPortrait" android:screenOrientation="sensorPortrait"
tools:ignore="LockedOrientationActivity" tools:ignore="LockedOrientationActivity" />
android:parentActivityName=".SettingsActivity" /> <activity
android:name=".LicenseActivity"
<activity android:name=".LicenseActivity" android:parentActivityName=".AboutActivity"
android:screenOrientation="sensorPortrait" android:screenOrientation="sensorPortrait"
tools:ignore="LockedOrientationActivity" tools:ignore="LockedOrientationActivity" />
android:parentActivityName=".AboutActivity" />
<provider <provider
android:name="androidx.core.content.FileProvider" android:name="androidx.core.content.FileProvider"

View File

@ -0,0 +1,28 @@
package com.h.pixeldroid
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.h.pixeldroid.fragments.CameraFragment
class CameraActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_camera)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setTitle(R.string.add_photo)
val cameraFragment = CameraFragment()
val arguments = Bundle()
arguments.putBoolean("CameraActivity", true)
cameraFragment.arguments = arguments
supportFragmentManager.beginTransaction()
.add(R.id.camera_activity_fragment, cameraFragment).commit()
}
override fun onSupportNavigateUp(): Boolean {
onBackPressed()
return true
}
}

View File

@ -88,7 +88,7 @@ class MainActivity : AppCompatActivity() {
} }
private fun setupDrawer() { private fun setupDrawer() {
main_toolbar.setNavigationOnClickListener { main_drawer_button.setOnClickListener{
drawer_layout.open() drawer_layout.open()
} }

View File

@ -103,11 +103,9 @@ class PhotoEditActivity : AppCompatActivity(), FilterListFragmentListener, EditI
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_photo_edit) setContentView(R.layout.activity_photo_edit)
//TODO move to xml: supportActionBar?.setTitle(R.string.toolbar_title_edit)
setSupportActionBar(toolbar) supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar!!.title = "Edit" supportActionBar?.setHomeButtonEnabled(true)
supportActionBar!!.setDisplayHomeAsUpEnabled(true)
supportActionBar!!.setHomeButtonEnabled(true)
val cropButton: FloatingActionButton = findViewById(R.id.cropImageButton) val cropButton: FloatingActionButton = findViewById(R.id.cropImageButton)
@ -267,7 +265,11 @@ class PhotoEditActivity : AppCompatActivity(), FilterListFragmentListener, EditI
private fun startCrop() { private fun startCrop() {
val file = File.createTempFile("temp_crop_img", ".png", cacheDir) val file = File.createTempFile("temp_crop_img", ".png", cacheDir)
val uCrop: UCrop = UCrop.of(initialUri!!, Uri.fromFile(file)) val options: UCrop.Options = UCrop.Options().apply {
setStatusBarColor(resources.getColor(R.color.colorPrimaryDark, theme))
setActiveControlsWidgetColor(resources.getColor(R.color.colorButtonBg, theme))
}
val uCrop: UCrop = UCrop.of(initialUri!!, Uri.fromFile(file)).withOptions(options)
uCrop.start(this) uCrop.start(this)
} }
@ -470,14 +472,19 @@ class PhotoEditActivity : AppCompatActivity(), FilterListFragmentListener, EditI
if(!save) { if(!save) {
sendBackImage(path) sendBackImage(path)
} else { } else {
MediaScannerConnection.scanFile( if(path.startsWith("file")) {
this, MediaScannerConnection.scanFile(
arrayOf(path.toUri().toFile().absolutePath), this,
null arrayOf(path.toUri().toFile().absolutePath),
null
) { path, uri -> ) { path, uri ->
if(uri == null) { if (uri == null) {
Log.e("NEW IMAGE SCAN FAILED", "Tried to scan $path, but it failed") Log.e(
"NEW IMAGE SCAN FAILED",
"Tried to scan $path, but it failed"
)
}
} }
} }

View File

@ -4,6 +4,7 @@ import android.app.Application
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.h.pixeldroid.di.* import com.h.pixeldroid.di.*
import com.h.pixeldroid.utils.ThemeUtils import com.h.pixeldroid.utils.ThemeUtils
import com.mikepenz.iconics.Iconics
import org.ligi.tracedroid.TraceDroid import org.ligi.tracedroid.TraceDroid
@ -23,7 +24,9 @@ class Pixeldroid: Application() {
.databaseModule(DatabaseModule(applicationContext)) .databaseModule(DatabaseModule(applicationContext))
.aPIModule(APIModule()) .aPIModule(APIModule())
.build() .build()
mApplicationComponent.inject(this); mApplicationComponent.inject(this)
Iconics.init(applicationContext)
} }
fun getAppComponent(): ApplicationComponent { fun getAppComponent(): ApplicationComponent {

View File

@ -1,7 +1,6 @@
package com.h.pixeldroid package com.h.pixeldroid
import android.app.Activity import android.app.Activity
import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
@ -26,7 +25,6 @@ import com.h.pixeldroid.interfaces.PostCreationListener
import com.h.pixeldroid.objects.Attachment import com.h.pixeldroid.objects.Attachment
import com.h.pixeldroid.objects.Instance import com.h.pixeldroid.objects.Instance
import com.h.pixeldroid.objects.Status import com.h.pixeldroid.objects.Status
import com.h.pixeldroid.utils.DBUtils
import com.h.pixeldroid.utils.ProgressRequestBody import com.h.pixeldroid.utils.ProgressRequestBody
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
@ -39,9 +37,11 @@ import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
import javax.inject.Inject import javax.inject.Inject
class PostCreationActivity : AppCompatActivity(), PostCreationListener { private val TAG = "Post Creation Activity"
private val MORE_PICTURES_REQUEST_CODE = 0xffff
private val TAG = "Post Creation Activity"
class PostCreationActivity : AppCompatActivity(), PostCreationListener {
private lateinit var recycler : RecyclerView private lateinit var recycler : RecyclerView
private lateinit var adapter : PostCreationAdapter private lateinit var adapter : PostCreationAdapter
@ -74,16 +74,19 @@ class PostCreationActivity : AppCompatActivity(), PostCreationListener {
(this.application as Pixeldroid).getAppComponent().inject(this) (this.application as Pixeldroid).getAppComponent().inject(this)
// load images // get image URIs
posts = intent.getStringArrayListExtra("pictures_uri")!! if(intent.clipData != null) {
val count = intent.clipData!!.itemCount
progressList = posts.map { 0 } as ArrayList<Int> for (i in 0 until count) {
muListOfIds = posts.map { "" }.toMutableList() val imageUri: String = intent.clipData!!.getItemAt(i).uri.toString()
posts.add(imageUri)
}
}
user = db.userDao().getActiveUser() user = db.userDao().getActiveUser()
val instances = db.instanceDao().getAll() val instances = db.instanceDao().getAll()
maxLength = if (user!=null){ maxLength = if (user != null){
val thisInstances = val thisInstances =
instances.filter { instanceDatabaseEntity -> instances.filter { instanceDatabaseEntity ->
instanceDatabaseEntity.uri.contains(user!!.instance_uri) instanceDatabaseEntity.uri.contains(user!!.instance_uri)
@ -100,12 +103,14 @@ class PostCreationActivity : AppCompatActivity(), PostCreationListener {
// TODO // TODO
//upload the picture and display progress while doing so //upload the picture and display progress while doing so
muListOfIds = posts.map { "" }.toMutableList()
progressList = posts.map { 0 } as ArrayList<Int>
upload() upload()
adapter = PostCreationAdapter(posts) adapter = PostCreationAdapter(posts)
adapter.listener = this adapter.listener = this
recycler = findViewById(R.id.image_grid) recycler = findViewById(R.id.image_grid)
recycler.layoutManager = GridLayoutManager(this, if (posts.size > 2) 2 else 1) recycler.layoutManager = GridLayoutManager(this, 3)
recycler.adapter = adapter recycler.adapter = adapter
// get the description and send the post // get the description and send the post
@ -126,8 +131,8 @@ class PostCreationActivity : AppCompatActivity(), PostCreationListener {
val textField = findViewById<TextInputEditText>(R.id.new_post_description_input_field) val textField = findViewById<TextInputEditText>(R.id.new_post_description_input_field)
val content = textField.text.toString() val content = textField.text.toString()
if (content.length > maxLength) { if (content.length > maxLength) {
// error, too much characters // error, too many characters
textField.error = "Description must contain $maxLength characters at most." textField.error = getString(R.string.description_max_characters).format(maxLength)
return false return false
} }
// store the description // store the description
@ -135,9 +140,26 @@ class PostCreationActivity : AppCompatActivity(), PostCreationListener {
return true return true
} }
private fun upload() { /**
for ((index, post) in posts.withIndex()) { * Uploads the images that are in the [posts] array.
val imageUri = Uri.parse(post) * Keeps track of them in the [progressList] (for the upload progress), and the [muListOfIds]
* (for the list of ids of the uploads).
* @param newImagesStartingIndex is the index in the [posts] array we want to start uploading at.
* Indices before this are already uploading, or done uploading, from before.
* @param editedImage contains the index of the image that was edited. If set, other images are
* not uploaded again: they should already be uploading, or be done uploading, from before.
*/
private fun upload(newImagesStartingIndex: Int = 0, editedImage: Int? = null) {
enableButton(false)
uploadProgressBar.visibility = View.VISIBLE
upload_completed_textview.visibility = View.INVISIBLE
val range: IntRange = if(editedImage == null){
newImagesStartingIndex until posts.size
} else IntRange(editedImage, editedImage)
for (index in range) {
val imageUri = Uri.parse(posts[index])
val imageInputStream = contentResolver.openInputStream(imageUri)!! val imageInputStream = contentResolver.openInputStream(imageUri)!!
val size = val size =
@ -237,7 +259,7 @@ class PostCreationActivity : AppCompatActivity(), PostCreationListener {
if(enable){ if(enable){
posting_progress_bar.visibility = View.GONE posting_progress_bar.visibility = View.GONE
post_creation_send_button.visibility = View.VISIBLE post_creation_send_button.visibility = View.VISIBLE
} else{ } else {
posting_progress_bar.visibility = View.VISIBLE posting_progress_bar.visibility = View.VISIBLE
post_creation_send_button.visibility = View.GONE post_creation_send_button.visibility = View.GONE
} }
@ -259,51 +281,85 @@ class PostCreationActivity : AppCompatActivity(), PostCreationListener {
if (resultCode == Activity.RESULT_OK && data != null) { if (resultCode == Activity.RESULT_OK && data != null) {
posts[positionResult] = data.getStringExtra("result")!! posts[positionResult] = data.getStringExtra("result")!!
adapter.notifyItemChanged(positionResult) adapter.notifyItemChanged(positionResult)
muListOfIds.clear() muListOfIds[positionResult] = ""
upload() progressList[positionResult] = 0
} upload(editedImage = positionResult)
else if(resultCode == Activity.RESULT_CANCELED){ } else if(resultCode == Activity.RESULT_CANCELED){
Toast.makeText(applicationContext, "Edition cancelled", Toast.LENGTH_SHORT).show() Toast.makeText(applicationContext, "Editing cancelled", Toast.LENGTH_SHORT).show()
} else { } else {
Toast.makeText(applicationContext, "Error while editing", Toast.LENGTH_SHORT).show() Toast.makeText(applicationContext, "Error while editing", Toast.LENGTH_SHORT).show()
} }
} else if (requestCode == MORE_PICTURES_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK && data?.clipData != null) {
val count = data.clipData!!.itemCount
for (i in 0 until count) {
val imageUri: String = data.clipData!!.getItemAt(i).uri.toString()
posts.add(imageUri)
progressList.add(0)
muListOfIds.add(i, "")
}
adapter.notifyDataSetChanged()
upload(newImagesStartingIndex = posts.size - count)
} else if(resultCode == Activity.RESULT_CANCELED){
Toast.makeText(applicationContext, "Adding images", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(applicationContext, "Error while adding images", Toast.LENGTH_SHORT).show()
}
} }
} }
class PostCreationAdapter(private val posts: ArrayList<String>): RecyclerView.Adapter<PostCreationAdapter.ViewHolder>() { inner class PostCreationAdapter(private val posts: ArrayList<String>): RecyclerView.Adapter<PostCreationAdapter.ViewHolder>() {
private var context: Context? = null
var listener: PostCreationListener? = null var listener: PostCreationListener? = null
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
context = parent.context val view =
val view = LayoutInflater.from(parent.context) if(viewType == 0) LayoutInflater.from(parent.context)
.inflate(R.layout.image_album_creation, parent, false) .inflate(R.layout.image_album_creation, parent, false)
else LayoutInflater.from(parent.context)
.inflate(R.layout.add_more_album_creation, parent, false)
return ViewHolder(view) return ViewHolder(view)
} }
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun getItemViewType(position: Int): Int {
Log.d("test", "binded") if(position == posts.size) return 1
holder.bind() return 0
} }
override fun getItemCount(): Int = posts.size override fun onBindViewHolder(holder: ViewHolder, position: Int) {
if(position != posts.size) {
holder.bindImage()
} else{
holder.bindPlusButton()
}
}
override fun getItemCount(): Int = posts.size + 1
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind() { fun bindImage() {
val image = Uri.parse(posts[adapterPosition]) val image = Uri.parse(
posts[adapterPosition]
)
// load image // load image
Glide.with(context!!) Glide.with(itemView.context)
.load(image) .load(image)
.centerCrop() .centerCrop()
.into(itemView.galleryImage) .into(itemView.galleryImage)
// adding click or tap handler for the image layout // adding click or tap handler for the image layout
itemView.galleryImage.setOnClickListener { itemView.setOnClickListener {
Log.d("test", "clicked")
listener?.onClick(adapterPosition) listener?.onClick(adapterPosition)
} }
}
fun bindPlusButton() {
itemView.setOnClickListener {
val intent = Intent(itemView.context, CameraActivity::class.java)
this@PostCreationActivity.startActivityForResult(intent, MORE_PICTURES_REQUEST_CODE)
}
} }
} }
} }
} }

View File

@ -0,0 +1,81 @@
package com.h.pixeldroid
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.View
import com.h.pixeldroid.db.AppDatabase
import com.h.pixeldroid.di.PixelfedAPIHolder
import com.h.pixeldroid.objects.Report
import com.h.pixeldroid.objects.Status
import kotlinx.android.synthetic.main.activity_report.*
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import javax.inject.Inject
class ReportActivity : AppCompatActivity() {
@Inject
lateinit var db: AppDatabase
@Inject
lateinit var apiHolder: PixelfedAPIHolder
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_report)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setTitle(R.string.report)
val status = intent.getSerializableExtra(Status.POST_TAG) as Status?
(this.application as Pixeldroid).getAppComponent().inject(this)
//get the currently active user
val user = db.userDao().getActiveUser()
report_target_textview.text = getString(R.string.report_target).format(status?.account?.acct)
reportButton.setOnClickListener{
reportButton.visibility = View.INVISIBLE
reportProgressBar.visibility = View.VISIBLE
textInputLayout.editText?.isEnabled = false
val accessToken = user?.accessToken.orEmpty()
val api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
api.report("Bearer $accessToken", status?.account?.id!!, listOf(status), textInputLayout.editText?.text.toString())
.enqueue(object : Callback<Report> {
override fun onResponse(
call: Call<Report>,
response: Response<Report>
) {
if (response.body() == null || !response.isSuccessful) {
textInputLayout.error = getString(R.string.report_error)
reportButton.visibility = View.VISIBLE
textInputLayout.editText?.isEnabled = true
reportProgressBar.visibility = View.GONE
} else {
reportProgressBar.visibility = View.GONE
reportButton.isEnabled = false
reportButton.text = getString(R.string.reported)
reportButton.visibility = View.VISIBLE
}
}
override fun onFailure(call: Call<Report>, t: Throwable) {
Log.e("REPORT:", t.toString())
}
})
}
}
override fun onSupportNavigateUp(): Boolean {
onBackPressed()
return true
}
}

View File

@ -20,9 +20,15 @@ class SearchActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_search) setContentView(R.layout.activity_search)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
var query = ""
if (Intent.ACTION_SEARCH == intent.action) {
query = intent.getStringExtra(SearchManager.QUERY).orEmpty()
}
var query = intent.getSerializableExtra("searchFeed") as String
query = query.trim() query = query.trim()
supportActionBar?.title = query
val searchType = when { val searchType = when {
query.startsWith("#") -> { query.startsWith("#") -> {
@ -41,6 +47,11 @@ class SearchActivity : AppCompatActivity() {
setupTabs(tabs, searchType) setupTabs(tabs, searchType)
} }
override fun onSupportNavigateUp(): Boolean {
onBackPressed()
return true
}
private fun createSearchTabs(query: String): Array<Fragment>{ private fun createSearchTabs(query: String): Array<Fragment>{
val searchFeedFragment = val searchFeedFragment =

View File

@ -54,13 +54,6 @@ class SettingsActivity : AppCompatActivity(), SharedPreferences.OnSharedPreferen
class SettingsFragment : PreferenceFragmentCompat() { class SettingsFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.root_preferences, rootKey) setPreferencesFromResource(R.xml.root_preferences, rootKey)
val button: Preference? = findPreference("about")
button?.onPreferenceClickListener =
Preference.OnPreferenceClickListener {
val intent = Intent(context, AboutActivity::class.java)
startActivity(intent)
true
}
} }
} }

View File

@ -266,4 +266,15 @@ interface PixelfedAPI {
fun discover( fun discover(
@Header("Authorization") authorization: String @Header("Authorization") authorization: String
) : Call<DiscoverPosts> ) : Call<DiscoverPosts>
@FormUrlEncoded
@POST("/api/v1/reports")
fun report(
@Header("Authorization") authorization: String,
@Field("account_id") account_id: String,
@Field("status_ids") status_ids: List<Status>,
@Field("comment") comment: String,
@Field("forward") forward: Boolean = true
) : Call<Report>
} }

View File

@ -24,6 +24,7 @@ interface ApplicationComponent {
fun inject(activity: PostCreationActivity?) fun inject(activity: PostCreationActivity?)
fun inject(activity: ProfileActivity?) fun inject(activity: ProfileActivity?)
fun inject(mainActivity: MainActivity?) fun inject(mainActivity: MainActivity?)
fun inject(activity: ReportActivity?)
fun inject(fragment: PostFragment) fun inject(fragment: PostFragment)
fun inject(fragment: SearchDiscoverFragment) fun inject(fragment: SearchDiscoverFragment)
fun inject(fragment: OfflineFeedFragment) fun inject(fragment: OfflineFeedFragment)

View File

@ -2,6 +2,7 @@ package com.h.pixeldroid.fragments
import android.Manifest import android.Manifest
import android.app.Activity import android.app.Activity
import android.content.ClipData
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.content.res.Configuration import android.content.res.Configuration
@ -23,13 +24,14 @@ import androidx.camera.view.PreviewView
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import androidx.core.view.setPadding import androidx.core.view.setPadding
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.RequestOptions
import com.h.pixeldroid.PhotoEditActivity
import com.h.pixeldroid.PostCreationActivity import com.h.pixeldroid.PostCreationActivity
import com.h.pixeldroid.CameraActivity
import com.h.pixeldroid.R import com.h.pixeldroid.R
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -39,6 +41,7 @@ import java.util.concurrent.Executors
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
import kotlin.properties.Delegates
// This is an arbitrary number we are using to keep track of the permission // This is an arbitrary number we are using to keep track of the permission
// request. Where an app has multiple context for requesting permission, // request. Where an app has multiple context for requesting permission,
@ -54,7 +57,10 @@ class CameraFragment : Fragment() {
private lateinit var container: ConstraintLayout private lateinit var container: ConstraintLayout
private lateinit var viewFinder: PreviewView private lateinit var viewFinder: PreviewView
private val REQUIRED_PERMISSIONS = arrayOf(Manifest.permission.CAMERA, Manifest.permission.READ_EXTERNAL_STORAGE) private val REQUIRED_PERMISSIONS = arrayOf(
Manifest.permission.CAMERA,
Manifest.permission.READ_EXTERNAL_STORAGE
)
private val PICK_IMAGE_REQUEST = 1 private val PICK_IMAGE_REQUEST = 1
private val CAPTURE_IMAGE_REQUEST = 2 private val CAPTURE_IMAGE_REQUEST = 2
@ -64,6 +70,8 @@ class CameraFragment : Fragment() {
private var imageCapture: ImageCapture? = null private var imageCapture: ImageCapture? = null
private var camera: Camera? = null private var camera: Camera? = null
private var inActivity by Delegates.notNull<Boolean>()
/** Blocking camera operations are performed using this executor */ /** Blocking camera operations are performed using this executor */
private lateinit var cameraExecutor: ExecutorService private lateinit var cameraExecutor: ExecutorService
@ -90,7 +98,8 @@ class CameraFragment : Fragment() {
*/ */
private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all { private fun allPermissionsGranted() = REQUIRED_PERMISSIONS.all {
ContextCompat.checkSelfPermission( ContextCompat.checkSelfPermission(
requireContext(), it) == PackageManager.PERMISSION_GRANTED requireContext(), it
) == PackageManager.PERMISSION_GRANTED
} }
override fun onDestroyView() { override fun onDestroyView() {
@ -105,8 +114,12 @@ class CameraFragment : Fragment() {
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle?): View? = savedInstanceState: Bundle?
inflater.inflate(R.layout.fragment_camera, container, false) ): View? {
inActivity = arguments?.getBoolean("CameraActivity") ?: false
return inflater.inflate(R.layout.fragment_camera, container, false)
}
private fun setGalleryThumbnail(uri: String) { private fun setGalleryThumbnail(uri: String) {
// Reference of the view that holds the gallery thumbnail // Reference of the view that holds the gallery thumbnail
@ -203,11 +216,12 @@ class CameraFragment : Fragment() {
// A variable number of use-cases can be passed here - // A variable number of use-cases can be passed here -
// camera provides access to CameraControl & CameraInfo // camera provides access to CameraControl & CameraInfo
camera = cameraProvider.bindToLifecycle( camera = cameraProvider.bindToLifecycle(
this, cameraSelector, preview, imageCapture) this, cameraSelector, preview, imageCapture
)
// Attach the viewfinder's surface provider to preview use case // Attach the viewfinder's surface provider to preview use case
preview?.setSurfaceProvider(viewFinder.createSurfaceProvider()) preview?.setSurfaceProvider(viewFinder.createSurfaceProvider())
} catch(exc: Exception) { } catch (exc: Exception) {
Log.e(TAG, "Use case binding failed", exc) Log.e(TAG, "Use case binding failed", exc)
} }
@ -246,7 +260,6 @@ class CameraFragment : Fragment() {
// In the background, load latest photo taken (if any) for gallery thumbnail // In the background, load latest photo taken (if any) for gallery thumbnail
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
// Find the last picture
// Find the last picture // Find the last picture
val projection = arrayOf( val projection = arrayOf(
MediaStore.Images.ImageColumns._ID, MediaStore.Images.ImageColumns._ID,
@ -382,10 +395,29 @@ class CameraFragment : Fragment() {
} }
private fun startAlbumCreation(uris: ArrayList<String>) { private fun startAlbumCreation(uris: ArrayList<String>) {
startActivity(
Intent(activity, PostCreationActivity::class.java) val intent = Intent(requireActivity(), PostCreationActivity::class.java)
.putExtra("pictures_uri", uris) .apply {
) uris.forEach{
//Why are we using ClipData here? Because the FLAG_GRANT_READ_URI_PERMISSION
//needs to be applied to the URIs, and this flag flag only applies to the
//Intent's data and any URIs specified in its ClipData.
if(clipData == null){
clipData = ClipData("", emptyArray(), ClipData.Item(it.toUri()))
} else {
clipData!!.addItem(ClipData.Item(it.toUri()))
}
}
addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
if(inActivity){
requireActivity().setResult(Activity.RESULT_OK, intent)
requireActivity().finish()
} else {
startActivity(intent)
}
} }
companion object { companion object {

View File

@ -50,11 +50,14 @@ class FilterListFragment : Fragment(), FilterListFragmentListener {
recyclerView.addItemDecoration(SpaceItemDecoration(space)) recyclerView.addItemDecoration(SpaceItemDecoration(space))
recyclerView.adapter = adapter recyclerView.adapter = adapter
displayImage(null)
return view return view
} }
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
displayImage(null)
}
private fun displayImage(bitmap: Bitmap?) { private fun displayImage(bitmap: Bitmap?) {
val r = Runnable { val r = Runnable {
val tbImage: Bitmap = (if (bitmap == null) { val tbImage: Bitmap = (if (bitmap == null) {
@ -74,10 +77,10 @@ class FilterListFragment : Fragment(), FilterListFragmentListener {
}) })
?: return@Runnable ?: return@Runnable
setupFilter(tbImage) if(activity != null) setupFilter(tbImage)
tbItemList.addAll(ThumbnailsManager.processThumbs(activity)) if(context != null) tbItemList.addAll(ThumbnailsManager.processThumbs(context))
requireActivity().runOnUiThread{ adapter.notifyDataSetChanged() } activity?.runOnUiThread{ adapter.notifyDataSetChanged() }
} }
Thread(r).start() Thread(r).start()
@ -93,7 +96,7 @@ class FilterListFragment : Fragment(), FilterListFragmentListener {
tbItem.filterName = tbItem.filter.name tbItem.filterName = tbItem.filter.name
ThumbnailsManager.addThumb(tbItem) ThumbnailsManager.addThumb(tbItem)
val filters = FilterPack.getFilterPack(requireActivity()) val filters = FilterPack.getFilterPack(context)
for (filter in filters) { for (filter in filters) {
val item = ThumbnailItem() val item = ThumbnailItem()

View File

@ -1,6 +1,5 @@
package com.h.pixeldroid.fragments package com.h.pixeldroid.fragments
import android.Manifest
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import android.os.Bundle import android.os.Bundle
@ -9,18 +8,14 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import android.widget.PopupMenu
import android.widget.Toast
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.google.android.material.snackbar.Snackbar
import com.h.pixeldroid.R import com.h.pixeldroid.R
import com.h.pixeldroid.utils.ImageUtils import kotlinx.android.synthetic.main.fragment_image.*
import com.karumi.dexter.Dexter
import com.karumi.dexter.listener.PermissionDeniedResponse
import com.karumi.dexter.listener.PermissionGrantedResponse
import com.karumi.dexter.listener.single.BasePermissionListener
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val IMG_URL = "imgurl" private const val IMG_URL = "imgurl"
private const val IMG_DESCRIPTION = "imgdescription"
private const val RQST_BLDR = "rqstbldr" private const val RQST_BLDR = "rqstbldr"
/** /**
@ -30,11 +25,13 @@ private const val RQST_BLDR = "rqstbldr"
*/ */
class ImageFragment : Fragment() { class ImageFragment : Fragment() {
private lateinit var imgUrl: String private lateinit var imgUrl: String
private lateinit var imgDescription: String
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
arguments?.let { arguments?.let {
imgUrl = it.getString(IMG_URL)!! imgUrl = it.getString(IMG_URL)!!
imgDescription = it.getString(IMG_DESCRIPTION)!!.ifEmpty { getString(R.string.no_description) }
} }
} }
@ -45,49 +42,10 @@ class ImageFragment : Fragment() {
val view = inflater.inflate(R.layout.fragment_image, container, false) val view = inflater.inflate(R.layout.fragment_image, container, false)
view.findViewById<ImageView>(R.id.imageImageView).setOnLongClickListener { view.findViewById<ImageView>(R.id.imageImageView).setOnLongClickListener {
PopupMenu(view.context, it).apply { Snackbar.make(it, imgDescription, Snackbar.LENGTH_SHORT).show()
setOnMenuItemClickListener { item ->
when (item.itemId) {
R.id.image_popup_menu_save_to_gallery -> {
Dexter.withContext(view.context)
.withPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
.withListener(object: BasePermissionListener() {
override fun onPermissionDenied(p0: PermissionDeniedResponse?) {
Toast.makeText(view.context,
view.context.getString(R.string.write_permission_download_pic),
Toast.LENGTH_SHORT).show()
}
override fun onPermissionGranted(p0: PermissionGrantedResponse?) {
ImageUtils.downloadImage(requireActivity(), imgUrl)
}
}).check()
true
}
R.id.image_popup_menu_share_picture -> {
Dexter.withContext(view.context)
.withPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
.withListener(object: BasePermissionListener() {
override fun onPermissionDenied(p0: PermissionDeniedResponse?) {
Toast.makeText(view.context,
view.context.getString(R.string.write_permission_share_pic),
Toast.LENGTH_SHORT).show()
}
override fun onPermissionGranted(p0: PermissionGrantedResponse?) {
ImageUtils.downloadImage(requireActivity(), imgUrl, share = true)
}
}).check()
true
}
else -> false
}
}
inflate(R.menu.image_popup_menu)
show()
}
true true
} }
// Inflate the layout for this fragment // Inflate the layout for this fragment
return view return view
} }
@ -100,6 +58,7 @@ class ImageFragment : Fragment() {
.placeholder(ColorDrawable(Color.GRAY)) .placeholder(ColorDrawable(Color.GRAY))
.load(imgUrl) .load(imgUrl)
.into(view.findViewById(R.id.imageImageView)!!) .into(view.findViewById(R.id.imageImageView)!!)
imageImageView.contentDescription = imgDescription
} }
companion object { companion object {
@ -111,10 +70,11 @@ class ImageFragment : Fragment() {
* @return A new instance of fragment ImageFragment. * @return A new instance of fragment ImageFragment.
*/ */
@JvmStatic @JvmStatic
fun newInstance(imageUrl: String) = fun newInstance(imageUrl: String, imageDescription: String) =
ImageFragment().apply { ImageFragment().apply {
arguments = Bundle().apply { arguments = Bundle().apply {
putString(IMG_URL, imageUrl) putString(IMG_URL, imageUrl)
putString(IMG_DESCRIPTION, imageDescription)
} }
} }
} }

View File

@ -39,14 +39,6 @@ class PostFragment : Fragment() {
.asDrawable().fitCenter() .asDrawable().fitCenter()
.placeholder(ColorDrawable(Color.GRAY)) .placeholder(ColorDrawable(Color.GRAY))
currentStatus?.setupPost(root, picRequest, this, statusDomain, true)
//Setup arguments needed for the onclicklisteners
val holder = PostViewHolder(
root,
requireContext()
)
(requireActivity().application as Pixeldroid).getAppComponent().inject(this) (requireActivity().application as Pixeldroid).getAppComponent().inject(this)
val user = db.userDao().getActiveUser() val user = db.userDao().getActiveUser()
@ -54,21 +46,17 @@ class PostFragment : Fragment() {
val accessToken = user?.accessToken.orEmpty() val accessToken = user?.accessToken.orEmpty()
val api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db) val api = apiHolder.api ?: apiHolder.setDomainToCurrentUser(db)
currentStatus?.setDescription(root, api, "Bearer $accessToken") currentStatus?.setupPost(root, picRequest, this, statusDomain, true)
//Activate onclickListeners val holder = PostViewHolder(
currentStatus?.activateLiker(holder, api, "Bearer $accessToken", root,
currentStatus.favourited ?: false root.context
) )
currentStatus?.activateReblogger(holder, api, "Bearer $accessToken",
currentStatus.reblogged ?: false
)
currentStatus?.activateCommenter(holder, api, "Bearer $accessToken")
currentStatus?.showComments(holder, api, "Bearer $accessToken")
//Activate double tap liking currentStatus?.activateButtons(holder, api, "Bearer $accessToken")
currentStatus?.activateDoubleTapLiker(holder, api, "Bearer $accessToken")
return root return root
} }
} }

View File

@ -1,19 +1,21 @@
package com.h.pixeldroid.fragments package com.h.pixeldroid.fragments
import android.app.SearchManager
import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Button import android.widget.*
import android.widget.EditText import androidx.appcompat.widget.SearchView
import android.widget.ImageView import androidx.core.content.ContextCompat.getSystemService
import android.widget.ProgressBar
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import com.google.android.material.textview.MaterialTextView
import com.h.pixeldroid.Pixeldroid import com.h.pixeldroid.Pixeldroid
import com.h.pixeldroid.PostActivity import com.h.pixeldroid.PostActivity
import com.h.pixeldroid.R import com.h.pixeldroid.R
@ -26,6 +28,12 @@ import com.h.pixeldroid.objects.DiscoverPosts
import com.h.pixeldroid.objects.Status import com.h.pixeldroid.objects.Status
import com.h.pixeldroid.utils.DBUtils import com.h.pixeldroid.utils.DBUtils
import com.h.pixeldroid.utils.ImageConverter import com.h.pixeldroid.utils.ImageConverter
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial
import com.mikepenz.iconics.utils.padding
import com.mikepenz.iconics.utils.paddingDp
import com.mikepenz.iconics.utils.sizeDp
import kotlinx.android.synthetic.main.fragment_search.*
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
@ -55,22 +63,30 @@ class SearchDiscoverFragment : Fragment() {
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View? {
val view = inflater.inflate(R.layout.fragment_search, container, false) val view = inflater.inflate(R.layout.fragment_search, container, false)
val button = view.findViewById<Button>(R.id.searchButton) val search = view.findViewById<SearchView>(R.id.search)
val search = view.findViewById<EditText>(R.id.searchEditText)
(requireActivity().application as Pixeldroid).getAppComponent().inject(this) (requireActivity().application as Pixeldroid).getAppComponent().inject(this)
button.setOnClickListener {
val intent = Intent(context, SearchActivity::class.java) //Configure the search widget (see https://developer.android.com/guide/topics/search/search-dialog#ConfiguringWidget)
intent.putExtra("searchFeed", search.text.toString()) val searchManager = requireActivity().getSystemService(Context.SEARCH_SERVICE) as SearchManager
startActivity(intent) search.setSearchableInfo(searchManager.getSearchableInfo(requireActivity().componentName))
}
search.isSubmitButtonEnabled = true
// Set posts RecyclerView as a grid with 3 columns // Set posts RecyclerView as a grid with 3 columns
recycler = view.findViewById(R.id.discoverList) recycler = view.findViewById(R.id.discoverList)
recycler.layoutManager = GridLayoutManager(requireContext(), 3) recycler.layoutManager = GridLayoutManager(requireContext(), 3)
adapter = DiscoverRecyclerViewAdapter() adapter = DiscoverRecyclerViewAdapter()
recycler.adapter = adapter recycler.adapter = adapter
val discoverText = view.findViewById<TextView>(R.id.discoverText)
discoverText.setCompoundDrawables(IconicsDrawable(requireContext(), GoogleMaterial.Icon.gmd_explore).apply {
sizeDp = 24
paddingDp = 20
}, null, null, null)
return view return view
} }

View File

@ -1,5 +1,6 @@
package com.h.pixeldroid.fragments.feeds package com.h.pixeldroid.fragments.feeds
import android.annotation.SuppressLint
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
@ -43,7 +44,7 @@ open class AccountListFragment : FeedFragment() {
//RequestBuilder that is re-used for every image //RequestBuilder that is re-used for every image
profilePicRequest = Glide.with(this) profilePicRequest = Glide.with(this)
.asDrawable().apply(RequestOptions().circleCrop()) .asDrawable().dontAnimate().apply(RequestOptions().circleCrop())
.placeholder(R.drawable.ic_default_user) .placeholder(R.drawable.ic_default_user)
adapter = AccountsRecyclerViewAdapter() adapter = AccountsRecyclerViewAdapter()
@ -160,9 +161,11 @@ open class AccountListFragment : FeedFragment() {
override fun onBindViewHolder(holder : ViewHolder, position : Int) { override fun onBindViewHolder(holder : ViewHolder, position : Int) {
val account = getItem(position) ?: return val account = getItem(position) ?: return
profilePicRequest.load(account.avatar_static).into(holder.avatar) profilePicRequest.load(account.avatar).into(holder.avatar)
holder.username.text = account.username holder.username.text = account.username
@SuppressLint("SetTextI18n")
holder.acct.text = "@${account.acct}"
holder.mView.setOnClickListener { account.openProfile(context) } holder.mView.setOnClickListener { account.openProfile(context) }
} }
@ -170,6 +173,7 @@ open class AccountListFragment : FeedFragment() {
inner class ViewHolder(val mView : View) : RecyclerView.ViewHolder(mView) { inner class ViewHolder(val mView : View) : RecyclerView.ViewHolder(mView) {
val avatar : ImageView = mView.account_entry_avatar val avatar : ImageView = mView.account_entry_avatar
val username : TextView = mView.account_entry_username val username : TextView = mView.account_entry_username
val acct: TextView = mView.account_entry_acct
} }
override fun getPreloadItems(position : Int) : MutableList<Account> { override fun getPreloadItems(position : Int) : MutableList<Account> {

View File

@ -117,7 +117,7 @@ open class FeedFragment: Fragment() {
} }
/** /**
* Do nothing here, it is expected to pull to refresh to load newer notifications * Do nothing here, it is expected to pull to refresh to load newer items
*/ */
override fun loadBefore(params: LoadParams<ObjectId>, callback: LoadCallback<APIObject>) {} override fun loadBefore(params: LoadParams<ObjectId>, callback: LoadCallback<APIObject>) {}

View File

@ -172,8 +172,8 @@ class OfflineFeedFragment: Fragment() {
holder.itemView.postTabs.visibility = View.VISIBLE holder.itemView.postTabs.visibility = View.VISIBLE
val tabs : ArrayList<ImageFragment> = ArrayList() val tabs : ArrayList<ImageFragment> = ArrayList()
//Fill the tabs with each mediaAttachment //Fill the tabs with each mediaAttachment
for(media in post.media_urls) { for((index, media) in post.media_urls.withIndex()) {
tabs.add(ImageFragment.newInstance(media)) tabs.add(ImageFragment.newInstance(media, "Photo $index"))
} }
holder.itemView.postPager.adapter = object : FragmentStateAdapter(this@OfflineFeedFragment) { holder.itemView.postPager.adapter = object : FragmentStateAdapter(this@OfflineFeedFragment) {
override fun createFragment(position: Int): Fragment { override fun createFragment(position: Int): Fragment {

View File

@ -12,6 +12,7 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.paging.PagedList import androidx.paging.PagedList
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import at.connyduck.sparkbutton.SparkButton import at.connyduck.sparkbutton.SparkButton
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.ListPreloader import com.bumptech.glide.ListPreloader
@ -60,7 +61,7 @@ abstract class PostsFeedFragment : FeedFragment() {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
val content = makeContent() val content = makeContent()
content.observe(viewLifecycleOwner, content.observe(viewLifecycleOwner,
Observer { c -> { c ->
adapter.submitList(c) adapter.submitList(c)
//after a refresh is done we need to stop the pull to refresh spinner //after a refresh is done we need to stop the pull to refresh spinner
swipeRefreshLayout.isRefreshing = false swipeRefreshLayout.isRefreshing = false
@ -99,29 +100,12 @@ abstract class PostsFeedFragment : FeedFragment() {
val post = getItem(position) ?: return val post = getItem(position) ?: return
val metrics = context.resources.displayMetrics val metrics = context.resources.displayMetrics
//Limit the height of the different images //Limit the height of the different images
holder.profilePic.maxHeight = metrics.heightPixels holder.postPic.maxHeight = metrics.heightPixels * 3/4
holder.postPic.maxHeight = metrics.heightPixels
//Setup the post layout //Setup the post layout
post.setupPost(holder.postView, picRequest, this@PostsFeedFragment, domain, false) post.setupPost(holder.postView, picRequest, this@PostsFeedFragment, domain, false)
//Set the special HTML text post.activateButtons(holder, api, credential)
post.setDescription(holder.postView, api, credential)
//Activate liker
post.activateLiker(holder, api, credential, post.favourited ?: false)
//Activate double tap liking
post.activateDoubleTapLiker(holder, api, credential)
//Show comments
post.showComments(holder, api, credential)
//Activate Commenter
post.activateCommenter(holder, api, credential)
//Activate Reblogger
post.activateReblogger(holder, api ,credential, post.reblogged ?: false)
} }
override fun getPreloadItems(position: Int): MutableList<Status> { override fun getPreloadItems(position: Int): MutableList<Status> {
@ -160,4 +144,7 @@ class PostViewHolder(val postView: View, val context: android.content.Context) :
val postDate : TextView = postView.findViewById(R.id.postDate) val postDate : TextView = postView.findViewById(R.id.postDate)
val postDomain : TextView = postView.findViewById(R.id.postDomain) val postDomain : TextView = postView.findViewById(R.id.postDomain)
val sensitiveW : TextView = postView.findViewById(R.id.sensitiveWarning) val sensitiveW : TextView = postView.findViewById(R.id.sensitiveWarning)
val postPager : ViewPager2 = postView.findViewById(R.id.postPager)
val more : ImageButton = postView.findViewById(R.id.status_more)
} }

View File

@ -0,0 +1,7 @@
package com.h.pixeldroid.objects
import java.io.Serializable
data class Report(
val id: String
): Serializable

View File

@ -1,29 +1,36 @@
package com.h.pixeldroid.objects package com.h.pixeldroid.objects
import android.Manifest import android.Manifest
import android.app.DownloadManager
import android.content.Context import android.content.Context
import android.content.Intent
import android.database.Cursor
import android.graphics.ColorMatrixColorFilter import android.graphics.ColorMatrixColorFilter
import android.graphics.Typeface import android.graphics.Typeface
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Environment
import android.text.Spanned import android.text.Spanned
import android.text.method.LinkMovementMethod import android.text.method.LinkMovementMethod
import android.view.View import android.view.View
import android.view.View.GONE import android.view.View.GONE
import android.view.View.VISIBLE import android.view.View.VISIBLE
import android.widget.* import android.widget.*
import androidx.core.content.ContextCompat.startActivity
import androidx.core.net.toUri
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.adapter.FragmentStateAdapter
import com.bumptech.glide.RequestBuilder import com.bumptech.glide.RequestBuilder
import com.google.android.material.snackbar.Snackbar
import com.google.android.material.tabs.TabLayoutMediator import com.google.android.material.tabs.TabLayoutMediator
import com.h.pixeldroid.R import com.h.pixeldroid.R
import com.h.pixeldroid.ReportActivity
import com.h.pixeldroid.api.PixelfedAPI import com.h.pixeldroid.api.PixelfedAPI
import com.h.pixeldroid.fragments.ImageFragment import com.h.pixeldroid.fragments.ImageFragment
import com.h.pixeldroid.fragments.feeds.postFeeds.PostViewHolder import com.h.pixeldroid.fragments.feeds.postFeeds.PostViewHolder
import com.h.pixeldroid.utils.HtmlUtils.Companion.getDomain import com.h.pixeldroid.utils.HtmlUtils.Companion.getDomain
import com.h.pixeldroid.utils.HtmlUtils.Companion.parseHTMLText import com.h.pixeldroid.utils.HtmlUtils.Companion.parseHTMLText
import com.h.pixeldroid.utils.ImageConverter import com.h.pixeldroid.utils.ImageConverter
import com.h.pixeldroid.utils.ImageUtils.Companion.downloadImage
import com.h.pixeldroid.utils.PostUtils.Companion.censorColorMatrix import com.h.pixeldroid.utils.PostUtils.Companion.censorColorMatrix
import com.h.pixeldroid.utils.PostUtils.Companion.likePostCall import com.h.pixeldroid.utils.PostUtils.Companion.likePostCall
import com.h.pixeldroid.utils.PostUtils.Companion.postComment import com.h.pixeldroid.utils.PostUtils.Companion.postComment
@ -39,8 +46,9 @@ import com.karumi.dexter.listener.PermissionDeniedResponse
import com.karumi.dexter.listener.PermissionGrantedResponse import com.karumi.dexter.listener.PermissionGrantedResponse
import com.karumi.dexter.listener.single.BasePermissionListener import com.karumi.dexter.listener.single.BasePermissionListener
import kotlinx.android.synthetic.main.post_fragment.view.* import kotlinx.android.synthetic.main.post_fragment.view.*
import java.io.File
import java.io.Serializable import java.io.Serializable
import java.util.Date import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
/* /*
@ -115,39 +123,70 @@ data class Status(
return context.getString(R.string.shares).format(reblogs_count.toString()) return context.getString(R.string.shares).format(reblogs_count.toString())
} }
private fun getStatusDomain(domain : String) : String { private fun getStatusDomain(domain: String) : String {
val accountDomain = getDomain(account!!.url) val accountDomain = getDomain(account!!.url)
return if(getDomain(domain) == accountDomain) "" return if(getDomain(domain) == accountDomain) ""
else " from $accountDomain" else " from $accountDomain"
} }
private fun setupPostPics(rootView: View, request: RequestBuilder<Drawable>, homeFragment: Fragment) { private fun setupPostPics(
rootView: View,
request: RequestBuilder<Drawable>,
homeFragment: Fragment
) {
// Standard layout // Standard layout
rootView.postPicture.visibility = VISIBLE rootView.postPicture.visibility = VISIBLE
rootView.postPager.visibility = GONE rootView.postPager.visibility = GONE
rootView.postTabs.visibility = GONE rootView.postTabs.visibility = GONE
if (sensitive!!) {
setupSensitiveLayout(rootView, request, homeFragment) if(media_attachments?.size == 1) {
request.load(this.getPostUrl()).into(rootView.postPicture) request.load(this.getPostUrl()).into(rootView.postPicture)
val imgDescription = media_attachments[0].description.orEmpty().ifEmpty { rootView.context.getString(R.string.no_description) }
rootView.postPicture.contentDescription = imgDescription
} else { rootView.postPicture.setOnLongClickListener {
rootView.sensitiveWarning.visibility = GONE Snackbar.make(it, imgDescription, Snackbar.LENGTH_SHORT).show()
true
if(media_attachments?.size == 1) {
request.load(this.getPostUrl()).into(rootView.postPicture)
} else if(media_attachments?.size!! > 1) {
setupTabsLayout(rootView, request, homeFragment)
} }
imagePopUpMenu(rootView, homeFragment.requireActivity()) } else if(media_attachments?.size!! > 1) {
setupTabsLayout(rootView, request, homeFragment)
}
if (sensitive!!) {
setupSensitiveLayout(rootView)
} }
} }
private fun setupTabsLayout(rootView: View, request: RequestBuilder<Drawable>, homeFragment: Fragment) { private fun setupSensitiveLayout(view: View) {
// Set dark layout and warning message
view.sensitiveWarning.visibility = VISIBLE
view.postPicture.colorFilter = ColorMatrixColorFilter(censorColorMatrix())
fun uncensorPicture(view: View) {
view.sensitiveWarning.visibility = GONE
view.postPicture.colorFilter = ColorMatrixColorFilter(uncensorColorMatrix())
}
view.findViewById<TextView>(R.id.sensitiveWarning).setOnClickListener {
uncensorPicture(view)
}
view.findViewById<ImageView>(R.id.postPicture).setOnClickListener {
uncensorPicture(view)
}
}
private fun setupTabsLayout(
rootView: View,
request: RequestBuilder<Drawable>,
homeFragment: Fragment
) {
//Only show the viewPager and tabs //Only show the viewPager and tabs
rootView.postPicture.visibility = GONE rootView.postPicture.visibility = GONE
rootView.postPager.visibility = VISIBLE rootView.postPager.visibility = VISIBLE
@ -157,7 +196,7 @@ data class Status(
//Fill the tabs with each mediaAttachment //Fill the tabs with each mediaAttachment
for(media in media_attachments!!) { for(media in media_attachments!!) {
tabs.add(ImageFragment.newInstance(media.url!!)) tabs.add(ImageFragment.newInstance(media.url!!, media.description.orEmpty()))
} }
setupTabs(tabs, rootView, homeFragment) setupTabs(tabs, rootView, homeFragment)
@ -185,8 +224,8 @@ data class Status(
rootView: View, rootView: View,
request: RequestBuilder<Drawable>, request: RequestBuilder<Drawable>,
homeFragment: Fragment, homeFragment: Fragment,
domain : String, domain: String,
isActivity : Boolean isActivity: Boolean
) { ) {
//Setup username as a button that opens the profile //Setup username as a button that opens the profile
rootView.findViewById<TextView>(R.id.username).apply { rootView.findViewById<TextView>(R.id.username).apply {
@ -233,10 +272,8 @@ data class Status(
rootView.findViewById<LinearLayout>(R.id.commentIn).visibility = GONE rootView.findViewById<LinearLayout>(R.id.commentIn).visibility = GONE
} }
fun setDescription(rootView: View, api : PixelfedAPI, credential: String) { fun setDescription(rootView: View, api: PixelfedAPI, credential: String) {
val desc = rootView.findViewById<TextView>(R.id.description) rootView.findViewById<TextView>(R.id.description).apply {
desc.apply {
if (content.isNullOrBlank()) { if (content.isNullOrBlank()) {
visibility = GONE visibility = GONE
} else { } else {
@ -246,11 +283,35 @@ data class Status(
} }
} }
fun activateButtons(holder: PostViewHolder, api: PixelfedAPI, credential: String){
//Set the special HTML text
setDescription(holder.postView, api, credential)
//Activate onclickListeners
activateLiker(
holder, api, credential,
this.favourited ?: false
)
activateReblogger(
holder, api, credential,
this.reblogged ?: false
)
activateCommenter(holder, api, credential)
showComments(holder, api, credential)
//Activate double tap liking
activateDoubleTapLiker(holder, api, credential)
activateMoreButton(holder)
}
fun activateReblogger( fun activateReblogger(
holder : PostViewHolder, holder: PostViewHolder,
api : PixelfedAPI, api: PixelfedAPI,
credential: String, credential: String,
isReblogged : Boolean isReblogged: Boolean
) { ) {
holder.reblogger.apply { holder.reblogger.apply {
//Set initial button state //Set initial button state
@ -271,8 +332,165 @@ data class Status(
} }
} }
fun downloadImage(context: Context, url: String, view: View, share: Boolean = false) {
val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
val downloadUri = Uri.parse(url)
val title = url.substringAfterLast("/")
val request = DownloadManager.Request(downloadUri).apply {
setTitle(title)
if(!share) {
val directory = File(Environment.DIRECTORY_PICTURES)
if (!directory.exists()) {
directory.mkdirs()
}
setDestinationInExternalPublicDir(directory.toString(), title)
}
}
val downloadId = downloadManager.enqueue(request)
val query = DownloadManager.Query().setFilterById(downloadId)
Thread {
var msg = ""
var lastMsg = ""
var downloading = true
while (downloading) {
val cursor: Cursor = downloadManager.query(query)
cursor.moveToFirst()
if (cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))
== DownloadManager.STATUS_SUCCESSFUL
) {
downloading = false
}
val status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))
if (!share) {
msg = when (status) {
DownloadManager.STATUS_FAILED ->
context.getString(R.string.image_download_failed)
DownloadManager.STATUS_RUNNING ->
context.getString(R.string.image_download_downloading)
DownloadManager.STATUS_SUCCESSFUL ->
context.getString(R.string.image_download_success)
else -> ""
}
if (msg != lastMsg && msg != "") {
Snackbar.make(view, msg, Snackbar.LENGTH_SHORT).show()
lastMsg = msg
}
} else if (status == DownloadManager.STATUS_SUCCESSFUL) {
val ext = url.substringAfterLast(".", "*")
val path = cursor.getString(
cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)
)
val file = path.toUri()
val shareIntent: Intent = Intent.createChooser(Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_STREAM, file)
data = file
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
type = "image/$ext"
}, null)
context.startActivity(shareIntent)
}
cursor.close()
}
}.start()
}
fun activateMoreButton(holder: PostViewHolder){
holder.more.setOnClickListener {
PopupMenu(it.context, it).apply {
setOnMenuItemClickListener { item ->
when (item.itemId) {
R.id.post_more_menu_report -> {
val intent = Intent(it.context, ReportActivity::class.java)
intent.putExtra(POST_TAG, this@Status)
startActivity(it.context, intent, null)
true
}
R.id.post_more_menu_share_link -> {
val share = Intent.createChooser(Intent().apply {
action = Intent.ACTION_SEND
putExtra(Intent.EXTRA_TEXT, uri)
type = "text/plain"
putExtra(Intent.EXTRA_TITLE, content)
}, null)
startActivity(it.context, share, null)
true
}
R.id.post_more_menu_save_to_gallery -> {
Dexter.withContext(holder.context)
.withPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
.withListener(object : BasePermissionListener() {
override fun onPermissionDenied(p0: PermissionDeniedResponse?) {
Toast.makeText(
holder.context,
holder.context.getString(R.string.write_permission_download_pic),
Toast.LENGTH_SHORT
).show()
}
override fun onPermissionGranted(p0: PermissionGrantedResponse?) {
downloadImage(
holder.context,
media_attachments?.get(holder.postPager.currentItem)?.url
?: "",
holder.postView
)
}
}).check()
true
}
R.id.post_more_menu_share_picture -> {
Dexter.withContext(holder.context)
.withPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
.withListener(object : BasePermissionListener() {
override fun onPermissionDenied(p0: PermissionDeniedResponse?) {
Toast.makeText(
holder.context,
holder.context.getString(R.string.write_permission_share_pic),
Toast.LENGTH_SHORT
).show()
}
override fun onPermissionGranted(p0: PermissionGrantedResponse?) {
downloadImage(
holder.context,
media_attachments?.get(holder.postPager.currentItem)?.url
?: "",
holder.postView,
share = true,
)
}
}).check()
true
}
else -> false
}
}
inflate(R.menu.post_more_menu)
if(media_attachments.isNullOrEmpty()) {
//make sure to disable image-related things if there aren't any
menu.setGroupVisible(R.id.post_more_group_picture, false)
}
show()
}
}
}
fun activateDoubleTapLiker( fun activateDoubleTapLiker(
holder : PostViewHolder, holder: PostViewHolder,
api: PixelfedAPI, api: PixelfedAPI,
credential: String credential: String
) { ) {
@ -305,7 +523,7 @@ data class Status(
} }
fun activateLiker( fun activateLiker(
holder : PostViewHolder, holder: PostViewHolder,
api: PixelfedAPI, api: PixelfedAPI,
credential: String, credential: String,
isLiked: Boolean isLiked: Boolean
@ -332,7 +550,7 @@ data class Status(
fun showComments( fun showComments(
holder : PostViewHolder, holder: PostViewHolder,
api: PixelfedAPI, api: PixelfedAPI,
credential: String credential: String
) { ) {
@ -353,19 +571,23 @@ data class Status(
} }
fun activateCommenter( fun activateCommenter(
holder : PostViewHolder, holder: PostViewHolder,
api: PixelfedAPI, api: PixelfedAPI,
credential: String credential: String
) { ) {
//Toggle comment button //Toggle comment button
toggleCommentInput(holder) toggleCommentInput(holder)
//Activate commenter //Activate commenterpostPicture
holder.submitCmnt.setOnClickListener { holder.submitCmnt.setOnClickListener {
val textIn = holder.comment.text val textIn = holder.comment.text
//Open text input //Open text input
if(textIn.isNullOrEmpty()) { if(textIn.isNullOrEmpty()) {
Toast.makeText(holder.context, holder.context.getString(R.string.empty_comment), Toast.LENGTH_SHORT).show() Toast.makeText(
holder.context,
holder.context.getString(R.string.empty_comment),
Toast.LENGTH_SHORT
).show()
} else { } else {
//Post the comment //Post the comment
@ -377,78 +599,4 @@ data class Status(
enum class Visibility : Serializable { enum class Visibility : Serializable {
public, unlisted, private, direct public, unlisted, private, direct
} }
private fun imagePopUpMenu(view: View, activity: FragmentActivity) {
val anchor = view.findViewById<FrameLayout>(R.id.post_fragment_image_popup_menu_anchor)
if (!media_attachments.isNullOrEmpty() && media_attachments.size == 1) {
view.findViewById<ImageView>(R.id.postPicture).setOnLongClickListener {
PopupMenu(view.context, anchor).apply {
setOnMenuItemClickListener { item ->
when (item.itemId) {
R.id.image_popup_menu_save_to_gallery -> {
Dexter.withContext(view.context)
.withPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
.withListener(object: BasePermissionListener() {
override fun onPermissionDenied(p0: PermissionDeniedResponse?) {
Toast.makeText(view.context, view.context.getString(R.string.write_permission_download_pic), Toast.LENGTH_SHORT).show()
}
override fun onPermissionGranted(p0: PermissionGrantedResponse?) {
downloadImage(activity, getPostUrl()!!)
}
}).check()
true
}
R.id.image_popup_menu_share_picture -> {
Dexter.withContext(view.context)
.withPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
.withListener(object: BasePermissionListener() {
override fun onPermissionDenied(p0: PermissionDeniedResponse?) {
Toast.makeText(view.context, view.context.getString(R.string.write_permission_share_pic), Toast.LENGTH_SHORT).show()
}
override fun onPermissionGranted(p0: PermissionGrantedResponse?) {
downloadImage(activity, getPostUrl()!!, share = true)
}
}).check()
true
}
else -> false
}
}
inflate(R.menu.image_popup_menu)
show()
}
true
}
}
}
private fun setupSensitiveLayout(view: View, request: RequestBuilder<Drawable>, homeFragment: Fragment) {
// Set dark layout and warning message
view.sensitiveWarning.visibility = VISIBLE
view.postPicture.colorFilter = ColorMatrixColorFilter(censorColorMatrix())
fun uncensorPicture(view: View) {
if (!media_attachments.isNullOrEmpty()) {
view.sensitiveWarning.visibility = GONE
view.postPicture.colorFilter = ColorMatrixColorFilter(uncensorColorMatrix())
if (media_attachments.size > 1)
setupTabsLayout(view, request, homeFragment)
}
imagePopUpMenu(view, homeFragment.requireActivity())
}
view.findViewById<TextView>(R.id.sensitiveWarning).setOnClickListener {
uncensorPicture(view)
}
view.findViewById<ImageView>(R.id.postPicture).setOnClickListener {
uncensorPicture(view)
}
}
} }

View File

@ -1,99 +0,0 @@
package com.h.pixeldroid.utils
import android.app.DownloadManager
import android.content.ContentValues
import android.content.Context
import android.content.Intent
import android.database.Cursor
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Environment
import android.provider.MediaStore.Images
import android.widget.Toast
import androidx.fragment.app.FragmentActivity
import com.h.pixeldroid.R
import java.io.File
class ImageUtils {
companion object {
fun downloadImage(activity: FragmentActivity, url: String, share: Boolean = false) {
val context = activity.applicationContext
var msg = ""
var lastMsg = ""
val directory = File(Environment.DIRECTORY_PICTURES)
if (!directory.exists()) {
directory.mkdirs()
}
val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE)
as DownloadManager
val downloadUri = Uri.parse(url)
val title = url.substring(url.lastIndexOf("/") + 1)
val ext = url.substring(url.lastIndexOf("."))
val request = DownloadManager.Request(downloadUri).apply {
setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI
or DownloadManager.Request.NETWORK_MOBILE)
setTitle(title)
setDestinationInExternalPublicDir(directory.toString(), title)
}
val downloadId = downloadManager.enqueue(request)
val query = DownloadManager.Query().setFilterById(downloadId)
Thread(Runnable {
var downloading = true
while (downloading) {
val cursor: Cursor = downloadManager.query(query)
cursor.moveToFirst()
if (cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))
== DownloadManager.STATUS_SUCCESSFUL) {
downloading = false
}
val status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))
if(!share) {
msg = when (status) {
DownloadManager.STATUS_FAILED ->
context.getString(R.string.image_download_failed)
DownloadManager.STATUS_RUNNING ->
context.getString(R.string.image_download_downloading)
DownloadManager.STATUS_SUCCESSFUL ->
context.getString(R.string.image_download_success)
else -> ""
}
if (msg != lastMsg && msg != "") {
activity.runOnUiThread {
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show()
}
lastMsg = msg
}
} else if (status == DownloadManager.STATUS_SUCCESSFUL) {
val icon: Bitmap = BitmapFactory.decodeFile(
Uri.parse(cursor.getString(
cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)
)).path
)
val intentShare = Intent(Intent.ACTION_SEND)
intentShare.type = "image/$ext"
val values = ContentValues()
values.put(Images.Media.TITLE, title)
values.put(Images.Media.MIME_TYPE, "image/$ext")
val uri: Uri = context.contentResolver.insert(
Images.Media.EXTERNAL_CONTENT_URI,
values
)!!
try {
val outstream = context.contentResolver.openOutputStream(uri)!!
icon.compress(Bitmap.CompressFormat.JPEG, 100, outstream)
outstream.close()
} catch(e: Exception) {
e.printStackTrace()
}
intentShare.putExtra(Intent.EXTRA_STREAM, uri)
activity.startActivity(Intent.createChooser(intentShare, context.getString(R.string.share_image)))
}
cursor.close()
}
}).start()
}
}
}

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M19,7v2.99s-1.99,0.01 -2,0L17,7h-3s0.01,-1.99 0,-2h3L17,2h2v3h3v2h-3zM16,11L16,8h-3L13,5L5,5c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2v-8h-3zM5,19l3,-4 2,3 3,-4 4,5L5,19z"
android:fillColor="#FFFFFF"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M19,7v2.99s-1.99,0.01 -2,0L17,7h-3s0.01,-1.99 0,-2h3L17,2h2v3h3v2h-3zM16,11L16,8h-3L13,5L5,5c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2v-8h-3zM5,19l3,-4 2,3 3,-4 4,5L5,19z"
android:fillColor="#000000"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2z"
android:fillColor="#000000"/>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2z"
android:fillColor="#000000"/>
</vector>

View File

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView <ImageView
android:id="@+id/account_entry_avatar" android:id="@+id/account_entry_avatar"
@ -11,20 +11,31 @@
android:layout_height="48dp" android:layout_height="48dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
android:contentDescription="TODO"
android:scaleType="centerCrop" android:scaleType="centerCrop"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:src="@drawable/ic_default_user" tools:src="@drawable/ic_default_user" />
android:contentDescription="TODO" />
<TextView <TextView
android:id="@+id/account_entry_username" android:id="@+id/account_entry_username"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginTop="28dp" android:textStyle="bold"
android:text="TextView"
app:layout_constraintStart_toEndOf="@+id/account_entry_avatar" app:layout_constraintStart_toEndOf="@+id/account_entry_avatar"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="@+id/account_entry_avatar"
tools:text="Username" />
<TextView
android:id="@+id/account_entry_acct"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
app:layout_constraintBottom_toBottomOf="@+id/account_entry_avatar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/account_entry_username"
app:layout_constraintTop_toBottomOf="@id/account_entry_username"
tools:text="\@username@domain.tld" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".CameraActivity">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/camera_activity_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -10,22 +10,27 @@
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/main_activity_main_linear_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<LinearLayout
<androidx.appcompat.widget.Toolbar android:id="@+id/main_activity_main_linear_layout"
android:id="@+id/main_toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@color/colorPrimaryTab" android:orientation="horizontal"
app:contentInsetStartWithNavigation="0dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintHorizontal_bias="0.0"
app:navigationContentDescription="Open drawer menu" app:layout_constraintStart_toStartOf="parent">
app:navigationIcon="@drawable/ic_baseline_menu_24">
<ImageButton
android:id="@+id/main_drawer_button"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="@color/colorPrimaryTab"
android:contentDescription="@string/open_drawer_menu"
android:padding="12dp"
android:src="@drawable/ic_baseline_menu_24" />
<com.google.android.material.tabs.TabLayout <com.google.android.material.tabs.TabLayout
android:id="@+id/tabs" android:id="@+id/tabs"
@ -36,19 +41,17 @@
app:tabMaxWidth="0dp" app:tabMaxWidth="0dp"
app:tabMode="fixed" app:tabMode="fixed"
app:tabUnboundedRipple="false" /> app:tabUnboundedRipple="false" />
</LinearLayout>
</androidx.appcompat.widget.Toolbar>
<androidx.viewpager2.widget.ViewPager2 <androidx.viewpager2.widget.ViewPager2
android:id="@+id/view_pager" android:id="@+id/view_pager"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
app:layout_constraintBottom_toTopOf="@+id/main_toolbar" app:layout_constraintBottom_toTopOf="@+id/main_activity_main_linear_layout"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -51,7 +51,7 @@
android:visibility="gone" android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/toolbar" /> app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.Guideline <androidx.constraintlayout.widget.Guideline
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -84,14 +84,4 @@
app:layout_constraintRight_toRightOf="@+id/right_guideline" app:layout_constraintRight_toRightOf="@+id/right_guideline"
app:layout_constraintBottom_toBottomOf="@+id/bottom_guideline" /> app:layout_constraintBottom_toBottomOf="@+id/bottom_guideline" />
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimaryActionBar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -17,15 +17,19 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<androidx.fragment.app.FragmentContainerView <ScrollView
android:id="@+id/postFragmentSingle" android:layout_width="match_parent"
android:layout_width="0dp" android:layout_height="match_parent">
android:layout_height="0dp" <androidx.fragment.app.FragmentContainerView
android:visibility="gone" android:id="@+id/postFragmentSingle"
app:layout_constraintBottom_toBottomOf="parent" android:layout_width="match_parent"
app:layout_constraintEnd_toEndOf="parent" android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent" android:visibility="gone"
app:layout_constraintTop_toTopOf="parent" > app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" >
</androidx.fragment.app.FragmentContainerView>
</ScrollView>
</androidx.fragment.app.FragmentContainerView>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -42,17 +42,29 @@
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
<com.mikepenz.iconics.view.IconicsTextView
android:id="@+id/upload_completed_textview"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:text="@string/media_upload_completed"
android:textColor="@android:color/holo_green_light"
android:textSize="16sp"
android:visibility="invisible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/image_grid" android:id="@+id/image_grid"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent" android:padding="16dp"
app:layout_constraintBottom_toTopOf="@id/postTextInputLayout"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toBottomOf="@id/upload_completed_textview" />
>
</androidx.recyclerview.widget.RecyclerView>
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/buttonConstraints" android:id="@+id/buttonConstraints"
@ -61,7 +73,7 @@
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/textInputLayout2"> app:layout_constraintTop_toTopOf="@id/postTextInputLayout">
<Button <Button
android:id="@+id/post_creation_send_button" android:id="@+id/post_creation_send_button"
@ -70,7 +82,7 @@
android:backgroundTint="@color/colorButtonBg" android:backgroundTint="@color/colorButtonBg"
android:enabled="false" android:enabled="false"
android:visibility="gone" android:visibility="gone"
android:text="@string/send" android:text="@string/post"
android:textColor="@color/colorButtonText" android:textColor="@color/colorButtonText"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
@ -93,12 +105,12 @@
style="?android:attr/progressBarStyleHorizontal" style="?android:attr/progressBarStyleHorizontal"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@id/textInputLayout2" app:layout_constraintBottom_toTopOf="@id/postTextInputLayout"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" /> app:layout_constraintStart_toStartOf="parent" />
<com.google.android.material.textfield.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/textInputLayout2" android:id="@+id/postTextInputLayout"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/description" android:hint="@string/description"
@ -121,16 +133,4 @@
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<com.mikepenz.iconics.view.IconicsTextView
android:id="@+id/upload_completed_textview"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:text="@string/media_upload_completed"
android:textColor="@android:color/holo_green_light"
android:textSize="16sp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -6,7 +6,7 @@
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".ProfileActivity"> tools:context=".ProfileActivity">
<ScrollView <androidx.core.widget.NestedScrollView
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@ -165,10 +165,11 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_margin="5dp" android:layout_margin="5dp"
android:nestedScrollingEnabled="false"
app:layoutManager="LinearLayoutManager" app:layoutManager="LinearLayoutManager"
tools:context=".fragments.ProfileFragment" tools:context=".fragments.ProfileFragment"
tools:listitem="@layout/fragment_profile_posts" /> tools:listitem="@layout/fragment_profile_posts" />
</LinearLayout> </LinearLayout>
</ScrollView> </androidx.core.widget.NestedScrollView>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ReportActivity">
<TextView
android:id="@+id/report_target_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@+id/textInputLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Reporting @user's post:" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/textInputLayout"
style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
app:counterEnabled="true"
app:counterMaxLength="1000"
app:layout_constraintBottom_toTopOf="@+id/reportButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="match_parent"
android:hint="@string/optional_report_comment"
android:inputType="text|textCapSentences|textMultiLine"
android:lines="8" />
</com.google.android.material.textfield.TextInputLayout>
<com.mikepenz.iconics.view.IconicsButton
android:id="@+id/reportButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/report"
android:textColor="@color/colorText"
android:backgroundTint="@color/colorPrimary"
app:iconGravity="end"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.75" />
<ProgressBar
android:id="@+id/reportProgressBar"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/reportButton"
app:layout_constraintEnd_toEndOf="@+id/reportButton"
app:layout_constraintStart_toStartOf="@+id/reportButton"
app:layout_constraintTop_toTopOf="@+id/reportButton" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<com.h.pixeldroid.utils.SquareLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foreground="?selectableItemBackground"
android:clickable="true"
android:focusable="true">
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerInParent="true"
android:layout_centerVertical="true"
android:background="@drawable/add_photo_alternate_black_24dp" />
</com.h.pixeldroid.utils.SquareLayout>

View File

@ -11,7 +11,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:adjustViewBounds="true" android:adjustViewBounds="true"
android:contentDescription="TODO" /> tools:ignore="ContentDescription" />
</FrameLayout> </FrameLayout>

View File

@ -1,24 +1,28 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<com.google.android.flexbox.FlexboxLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.gridlayout.widget.GridLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:flexWrap="wrap" app:columnCount="3">
app:justifyContent="center">
<androidx.cardview.widget.CardView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView <ImageView
android:id="@+id/postPreview" android:id="@+id/postPreview"
android:layout_width="100dp" android:layout_width="0dp"
android:layout_height="100dp" android:layout_height="0dp"
android:contentDescription="TODO" /> app:layout_constraintDimensionRatio="H,1:1"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:contentDescription="TODO"
android:padding="1dp"
app:layout_gravity="fill"
app:layout_rowWeight="1"
app:layout_columnWeight="1"/>
</androidx.cardview.widget.CardView> </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.gridlayout.widget.GridLayout>
</com.google.android.flexbox.FlexboxLayout>

View File

@ -1,50 +1,28 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"> android:layout_gravity="center_horizontal">
<com.google.android.material.textfield.TextInputLayout <androidx.appcompat.widget.SearchView
android:id="@+id/search" android:id="@+id/search"
android:layout_width="250dp"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:gravity="center"
android:hint="@string/search"
app:errorEnabled="true"
app:layout_constraintEnd_toStartOf="@+id/searchButton"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/searchEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:imeOptions="actionDone"
android:inputType="textUri" />
</com.google.android.material.textfield.TextInputLayout>
<Button
android:id="@+id/searchButton"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="20dp" app:iconifiedByDefault="false"
android:backgroundTint="@color/colorButtonBg"
android:text="@string/search"
android:textColor="@color/colorButtonText"
app:layout_constraintBottom_toBottomOf="@+id/search"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/search" /> app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:queryHint="@string/search" />
<ProgressBar <ProgressBar
android:id="@+id/discoverProgressBar" android:id="@+id/discoverProgressBar"
style="?android:attr/progressBarStyle" style="?android:attr/progressBarStyle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
@ -54,21 +32,33 @@
android:id="@+id/discoverRefreshLayout" android:id="@+id/discoverRefreshLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" android:layout_marginTop="10dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/search"> app:layout_constraintTop_toBottomOf="@+id/search">
<androidx.core.widget.NestedScrollView
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/discoverList"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent">
android:layout_marginLeft="16dp" <LinearLayout
android:layout_marginRight="16dp" android:layout_width="match_parent"
app:layoutManager="com.google.android.flexbox.FlexboxLayoutManager" android:layout_height="match_parent"
app:layout_constraintTop_toBottomOf="@+id/search" /> android:orientation="vertical">
<TextView
android:id="@+id/discoverText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:text="@string/discover"
android:layout_gravity="center_horizontal"/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/discoverList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false"/>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout> </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,23 +1,34 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<com.h.pixeldroid.utils.SquareLayout <com.h.pixeldroid.utils.SquareLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:foreground="?selectableItemBackground" android:foreground="?selectableItemBackground"
android:clickable="true" android:clickable="true"
android:focusable="true"> android:focusable="true">
<RelativeLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<ImageView <ImageView
android:id="@+id/galleryImage" android:id="@+id/galleryImage"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:scaleType="centerCrop"/> android:padding="8dp"
android:scaleType="centerCrop" />
<ImageView <ImageView
android:layout_width="30dp" android:layout_width="30dp"
android:layout_height="30dp" android:layout_height="30dp"
android:src="@drawable/ic_baseline_edit_24"/> android:layout_marginStart="16dp"
</RelativeLayout> android:layout_marginTop="16dp"
android:background="@drawable/circle_black_24dp"
android:backgroundTint="#7A3E3C3C"
android:foreground="@drawable/ic_baseline_edit_24"
android:foregroundGravity="center"
android:foregroundTint="#FFFFFF"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.h.pixeldroid.utils.SquareLayout> </com.h.pixeldroid.utils.SquareLayout>

View File

@ -4,17 +4,10 @@
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
xmlns:sparkbutton="http://schemas.android.com/apk/res-auto" xmlns:sparkbutton="http://schemas.android.com/apk/res-auto"
tools:context=".fragments.PostFragment"> tools:context=".fragments.PostFragment">
<androidx.cardview.widget.CardView
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -33,16 +26,18 @@
android:layout_marginEnd="10dp" android:layout_marginEnd="10dp"
android:src="@drawable/ic_default_user" android:src="@drawable/ic_default_user"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent"
android:contentDescription="@string/profile_picture" />
<TextView <TextView
android:id="@+id/username" android:id="@+id/username"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="12dp" android:layout_marginStart="12dp"
app:layout_constraintBottom_toBottomOf="@+id/profilePic" app:layout_constraintBottom_toBottomOf="@+id/profilePic"
app:layout_constraintStart_toEndOf="@+id/profilePic" app:layout_constraintStart_toEndOf="@+id/profilePic"
app:layout_constraintTop_toTopOf="@+id/profilePic"/> app:layout_constraintTop_toTopOf="@+id/profilePic"
tools:text="username" />
<TextView <TextView
android:id="@+id/postDomain" android:id="@+id/postDomain"
@ -51,14 +46,14 @@
android:textColor="#b3b3b3" android:textColor="#b3b3b3"
app:layout_constraintBottom_toBottomOf="@+id/profilePic" app:layout_constraintBottom_toBottomOf="@+id/profilePic"
app:layout_constraintStart_toEndOf="@+id/username" app:layout_constraintStart_toEndOf="@+id/username"
app:layout_constraintTop_toTopOf="@+id/profilePic"/> app:layout_constraintTop_toTopOf="@+id/profilePic"
tools:text=" from domain.tld" />
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/postConstraint" android:id="@+id/postConstraint"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="10dp" android:layout_marginTop="10dp"
android:adjustViewBounds="true"
app:layout_constraintTop_toBottomOf="@+id/profilePic"> app:layout_constraintTop_toBottomOf="@+id/profilePic">
<androidx.viewpager2.widget.ViewPager2 <androidx.viewpager2.widget.ViewPager2
@ -87,7 +82,7 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:src="@color/browser_actions_bg_grey" tools:src="@color/browser_actions_bg_grey"
android:longClickable="true" /> tools:ignore="ContentDescription" />
<FrameLayout <FrameLayout
android:id="@+id/post_fragment_image_popup_menu_anchor" android:id="@+id/post_fragment_image_popup_menu_anchor"
@ -106,11 +101,13 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:adjustViewBounds="true" android:adjustViewBounds="true"
android:gravity="center|center_horizontal|center_vertical" android:gravity="center|center_horizontal|center_vertical"
android:longClickable="true"
android:text="@string/cw_nsfw_hidden_media_n_click_to_show" android:text="@string/cw_nsfw_hidden_media_n_click_to_show"
android:textAppearance="@style/TextAppearance.AppCompat.Medium" android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:textColor="@color/ic_launcher_background" android:textColor="@color/ic_launcher_background"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@+id/postPicture" app:layout_constraintBottom_toBottomOf="@+id/postPicture"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/postTabs" app:layout_constraintTop_toBottomOf="@+id/postTabs"
tools:src="@color/browser_actions_bg_grey" /> tools:src="@color/browser_actions_bg_grey" />
@ -121,7 +118,6 @@
android:id="@+id/commenter" android:id="@+id/commenter"
android:layout_width="30dp" android:layout_width="30dp"
android:layout_height="30dp" android:layout_height="30dp"
android:importantForAccessibility="no"
android:padding="4dp" android:padding="4dp"
android:src="@drawable/ic_comment_empty" android:src="@drawable/ic_comment_empty"
app:layout_constraintBottom_toBottomOf="@+id/liker" app:layout_constraintBottom_toBottomOf="@+id/liker"
@ -137,7 +133,6 @@
android:layout_marginTop="4dp" android:layout_marginTop="4dp"
android:layout_marginBottom="4dp" android:layout_marginBottom="4dp"
android:clipToPadding="false" android:clipToPadding="false"
android:importantForAccessibility="no"
android:padding="4dp" android:padding="4dp"
sparkbutton:activeImage="@drawable/ic_like_full" sparkbutton:activeImage="@drawable/ic_like_full"
sparkbutton:iconSize="28dp" sparkbutton:iconSize="28dp"
@ -154,7 +149,6 @@
android:layout_width="30dp" android:layout_width="30dp"
android:layout_height="30dp" android:layout_height="30dp"
android:clipToPadding="false" android:clipToPadding="false"
android:importantForAccessibility="no"
android:padding="4dp" android:padding="4dp"
app:layout_constraintBottom_toBottomOf="@+id/commenter" app:layout_constraintBottom_toBottomOf="@+id/commenter"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
@ -166,6 +160,17 @@
sparkbutton:primaryColor="@color/share_blue" sparkbutton:primaryColor="@color/share_blue"
sparkbutton:secondaryColor="@color/black"/> sparkbutton:secondaryColor="@color/black"/>
<ImageButton
android:id="@+id/status_more"
android:layout_width="24dp"
android:layout_height="30dp"
android:layout_marginEnd="16dp"
android:contentDescription="@string/status_more_options"
android:padding="4dp"
style="?android:attr/actionOverflowButtonStyle"
app:layout_constraintBottom_toBottomOf="@+id/postDomain"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@+id/postDomain" />
<TextView <TextView
android:id="@+id/nlikes" android:id="@+id/nlikes"
@ -182,7 +187,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="50" android:layout_weight="50"
android:gravity="right" android:gravity="end"
app:layout_constraintEnd_toEndOf="@+id/reblogger" app:layout_constraintEnd_toEndOf="@+id/reblogger"
app:layout_constraintStart_toStartOf="@+id/reblogger" app:layout_constraintStart_toStartOf="@+id/reblogger"
app:layout_constraintTop_toBottomOf="@+id/reblogger" app:layout_constraintTop_toBottomOf="@+id/reblogger"
@ -214,7 +219,7 @@
android:textColor="#b3b3b3" android:textColor="#b3b3b3"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/description" app:layout_constraintTop_toBottomOf="@+id/description"
tools:text="time" /> tools:text="Yesterday" />
<LinearLayout <LinearLayout
android:id="@+id/commentIn" android:id="@+id/commentIn"
@ -234,7 +239,6 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:hint="@string/comment" android:hint="@string/comment"
android:importantForAutofill="no"
android:inputType="text" /> android:inputType="text" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
@ -262,15 +266,10 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="10dp" android:layout_marginStart="10dp"
android:layout_marginBottom="10dp" android:layout_marginBottom="10dp"
tools:text="TextView"/> tools:text="3 comments"/>
</LinearLayout> </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
</androidx.cardview.widget.CardView>
</FrameLayout> </FrameLayout>

View File

@ -1,18 +1,19 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<TextView <TextView
android:id="@+id/filter_name" android:id="@+id/filter_name"
android:text="FILTER_NAME"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="5dp"
android:layout_marginTop="5dp"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" /> android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
tools:text="FILTER_NAME" />
<ImageView <ImageView
android:id="@+id/thumbnail" android:id="@+id/thumbnail"
@ -20,5 +21,5 @@
android:adjustViewBounds="true" android:adjustViewBounds="true"
android:layout_width="80dp" android:layout_width="80dp"
android:layout_height="80dp" android:layout_height="80dp"
android:contentDescription="thumbnail of filter" /> android:contentDescription="Thumbnail of filter" />
</LinearLayout> </LinearLayout>

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/image_popup_menu_save_to_gallery"
android:title="@string/save_to_gallery"/>
<item android:id="@+id/image_popup_menu_share_picture"
android:title="@string/share_picture"/>
</menu>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/post_more_menu_report"
android:title="@string/report" />
<item android:id="@+id/post_more_menu_share_link"
android:title="@string/share_link" />
<!-- Group that should only be shown if there are pictures in the post -->
<group android:id="@+id/post_more_group_picture">
<item android:id="@+id/post_more_menu_share_picture"
android:title="@string/share_picture"/>
<item android:id="@+id/post_more_menu_save_to_gallery"
android:title="@string/save_to_gallery"/>
</group>
</menu>

View File

@ -15,7 +15,7 @@
<string name="auth_failed">فشلت المصادقة</string> <string name="auth_failed">فشلت المصادقة</string>
<string name="followed_notification">يتابعك %1$s</string> <string name="followed_notification">يتابعك %1$s</string>
<string name="description">الوصف…</string> <string name="description">الوصف…</string>
<string name="send">ارسل</string> <string name="post">ارسل</string>
<string name="whats_an_instance">ماذا نعني بمثيل الخادم؟</string> <string name="whats_an_instance">ماذا نعني بمثيل الخادم؟</string>
<string name="logout">الخروج</string> <string name="logout">الخروج</string>
<string name="tab_filters">الفلاتر</string> <string name="tab_filters">الفلاتر</string>

View File

@ -19,7 +19,7 @@
<string name="save_to_gallery">Desar a la galeria…</string> <string name="save_to_gallery">Desar a la galeria…</string>
<string name="logout">Tancar sessió</string> <string name="logout">Tancar sessió</string>
<string name="whats_an_instance">Què és una instància\?</string> <string name="whats_an_instance">Què és una instància\?</string>
<string name="send">enviar</string> <string name="post">enviar</string>
<string name="description">Descripció…</string> <string name="description">Descripció…</string>
<string name="liked_notification">%1$s ha dona\'t m\'agrada a la teva publicació</string> <string name="liked_notification">%1$s ha dona\'t m\'agrada a la teva publicació</string>
<string name="theme_title">Tema de l\'aplicació</string> <string name="theme_title">Tema de l\'aplicació</string>

View File

@ -13,7 +13,7 @@
<string name="mention_notification">%1$s hat dich erwähnt</string> <string name="mention_notification">%1$s hat dich erwähnt</string>
<string name="shared_notification">%1$s hat deinen Beitrag geteilt</string> <string name="shared_notification">%1$s hat deinen Beitrag geteilt</string>
<string name="liked_notification">%1$s hat deinen Beitrag favorisiert</string> <string name="liked_notification">%1$s hat deinen Beitrag favorisiert</string>
<string name="send">senden</string> <string name="post">senden</string>
<string name="whats_an_instance">Was ist eine Instanz\?</string> <string name="whats_an_instance">Was ist eine Instanz\?</string>
<string name="theme_title">Erscheinungsbild</string> <string name="theme_title">Erscheinungsbild</string>
<string name="description">Beschreibung…</string> <string name="description">Beschreibung…</string>

View File

@ -12,7 +12,7 @@
<string name="shared_notification">%1$s compartió tu publicación</string> <string name="shared_notification">%1$s compartió tu publicación</string>
<string name="liked_notification">%1$s le gustó tu publicación</string> <string name="liked_notification">%1$s le gustó tu publicación</string>
<string name="description">Descripción…</string> <string name="description">Descripción…</string>
<string name="send">enviar</string> <string name="post">enviar</string>
<string name="whats_an_instance">¿Qué es una instancia\?</string> <string name="whats_an_instance">¿Qué es una instancia\?</string>
<string name="logout">Cerrar sesión</string> <string name="logout">Cerrar sesión</string>
<string name="save_to_gallery">Guardar en la galería…</string> <string name="save_to_gallery">Guardar en la galería…</string>

View File

@ -9,7 +9,7 @@
<string name="save_to_gallery">Gorde galerian…</string> <string name="save_to_gallery">Gorde galerian…</string>
<string name="logout">Saioa Itxi</string> <string name="logout">Saioa Itxi</string>
<string name="whats_an_instance">Zer da instantzia bat\?</string> <string name="whats_an_instance">Zer da instantzia bat\?</string>
<string name="send">bidali</string> <string name="post">bidali</string>
<string name="description">Deskribapena…</string> <string name="description">Deskribapena…</string>
<string name="mention_notification">%1$s erabiltzaileak aipatu zaitu</string> <string name="mention_notification">%1$s erabiltzaileak aipatu zaitu</string>
<string name="followed_notification">%1$s jarraitzen hasi zaizu</string> <string name="followed_notification">%1$s jarraitzen hasi zaizu</string>

View File

@ -16,7 +16,7 @@
<string name="shared_notification">%1$s مطلب شما را هم‌رسانی کرد</string> <string name="shared_notification">%1$s مطلب شما را هم‌رسانی کرد</string>
<string name="liked_notification">%1$s مطلب شما را پسندید</string> <string name="liked_notification">%1$s مطلب شما را پسندید</string>
<string name="description">توضیحات…</string> <string name="description">توضیحات…</string>
<string name="send">بفرست</string> <string name="post">بفرست</string>
<string name="whats_an_instance">یک نمونه چیست؟</string> <string name="whats_an_instance">یک نمونه چیست؟</string>
<string name="logout">خروج</string> <string name="logout">خروج</string>
<string name="lbl_brightness">روشنایی</string> <string name="lbl_brightness">روشنایی</string>

View File

@ -9,7 +9,7 @@
<string name="shared_notification">%1$s a partagé votre publication</string> <string name="shared_notification">%1$s a partagé votre publication</string>
<string name="liked_notification">%1$s a aimé votre publication</string> <string name="liked_notification">%1$s a aimé votre publication</string>
<string name="description">Description…</string> <string name="description">Description…</string>
<string name="send">envoyer</string> <string name="post">envoyer</string>
<string name="whats_an_instance">Qu\'est-ce qu\'une instance \?</string> <string name="whats_an_instance">Qu\'est-ce qu\'une instance \?</string>
<string name="logout">Se déconnecter</string> <string name="logout">Se déconnecter</string>
<string name="save_to_gallery">Sauvegarder dans la galerie…</string> <string name="save_to_gallery">Sauvegarder dans la galerie…</string>

View File

@ -14,7 +14,7 @@
<string name="liked_notification">a %1$s gustoulle a publicación</string> <string name="liked_notification">a %1$s gustoulle a publicación</string>
<string name="shared_notification">%1$s comparteu a publicación</string> <string name="shared_notification">%1$s comparteu a publicación</string>
<string name="description">Descrición…</string> <string name="description">Descrición…</string>
<string name="send">enviar</string> <string name="post">enviar</string>
<string name="whats_an_instance">Que é unha instancia\?</string> <string name="whats_an_instance">Que é unha instancia\?</string>
<string name="logout">Saír</string> <string name="logout">Saír</string>
<string name="lbl_brightness">BRILLO</string> <string name="lbl_brightness">BRILLO</string>

View File

@ -14,7 +14,7 @@
<string name="mention_notification">%1$s ti ha menzionato</string> <string name="mention_notification">%1$s ti ha menzionato</string>
<string name="liked_notification">%1$s è piaciuto il tuo post</string> <string name="liked_notification">%1$s è piaciuto il tuo post</string>
<string name="tab_filters">FILTRI</string> <string name="tab_filters">FILTRI</string>
<string name="send">invia</string> <string name="post">invia</string>
<string name="lbl_brightness">LUMINOSITÀ</string> <string name="lbl_brightness">LUMINOSITÀ</string>
<string name="description">Descrizione…</string> <string name="description">Descrizione…</string>
<string name="whats_an_instance">Cos\'è un\'istanza\?</string> <string name="whats_an_instance">Cos\'è un\'istanza\?</string>

View File

@ -11,7 +11,7 @@
<string name="followed_notification">%1$s さんにフォローされました</string> <string name="followed_notification">%1$s さんにフォローされました</string>
<string name="liked_notification">%1$s さんがあなたの投稿をお気に入りに登録しました</string> <string name="liked_notification">%1$s さんがあなたの投稿をお気に入りに登録しました</string>
<string name="description">説明…</string> <string name="description">説明…</string>
<string name="send">送信</string> <string name="post">送信</string>
<string name="whats_an_instance">インスタンスとは?</string> <string name="whats_an_instance">インスタンスとは?</string>
<string name="logout">ログアウト</string> <string name="logout">ログアウト</string>
<string name="lbl_brightness">輝度</string> <string name="lbl_brightness">輝度</string>

View File

@ -11,7 +11,7 @@
<string name="menu_account">Mijn profiel</string> <string name="menu_account">Mijn profiel</string>
<string name="logout">Log uit</string> <string name="logout">Log uit</string>
<string name="whats_an_instance">Wat is een instance\?</string> <string name="whats_an_instance">Wat is een instance\?</string>
<string name="send">stuur</string> <string name="post">stuur</string>
<string name="description">Beschrijving…</string> <string name="description">Beschrijving…</string>
<string name="liked_notification">%1$s vond je bericht leuk</string> <string name="liked_notification">%1$s vond je bericht leuk</string>
<string name="shared_notification">%1$s heeft je bericht gedeeld</string> <string name="shared_notification">%1$s heeft je bericht gedeeld</string>

View File

@ -30,7 +30,7 @@
<string name="lbl_brightness">BRILHO</string> <string name="lbl_brightness">BRILHO</string>
<string name="logout">Sair</string> <string name="logout">Sair</string>
<string name="whats_an_instance">O que é uma instância\?</string> <string name="whats_an_instance">O que é uma instância\?</string>
<string name="send">enviar</string> <string name="post">enviar</string>
<string name="description">Descrição…</string> <string name="description">Descrição…</string>
<string name="liked_notification">%1$s curtiu o seu post</string> <string name="liked_notification">%1$s curtiu o seu post</string>
<string name="shared_notification">%1$s compartilhou o seu post</string> <string name="shared_notification">%1$s compartilhou o seu post</string>

View File

@ -8,7 +8,7 @@
<string name="followed_notification">%1$s подписался(-лась) на вас</string> <string name="followed_notification">%1$s подписался(-лась) на вас</string>
<string name="mention_notification">%1$s упомянул(а) вас</string> <string name="mention_notification">%1$s упомянул(а) вас</string>
<string name="shared_notification">%1$s поделился(-лась) вашим постом</string> <string name="shared_notification">%1$s поделился(-лась) вашим постом</string>
<string name="send">отправить</string> <string name="post">отправить</string>
<string name="logout">Выйти</string> <string name="logout">Выйти</string>
<string name="lbl_brightness">ЯРКОСТЬ</string> <string name="lbl_brightness">ЯРКОСТЬ</string>
<string name="whats_an_instance">Что такое инстанс\?</string> <string name="whats_an_instance">Что такое инстанс\?</string>

View File

@ -22,7 +22,7 @@
<string name="shared_notification">%1$s delande ditt inlägg</string> <string name="shared_notification">%1$s delande ditt inlägg</string>
<string name="liked_notification">%1$s gillar ditt inlägg</string> <string name="liked_notification">%1$s gillar ditt inlägg</string>
<string name="description">Beskrivning…</string> <string name="description">Beskrivning…</string>
<string name="send">skicka</string> <string name="post">skicka</string>
<string name="whats_an_instance">Vad är en instans\?</string> <string name="whats_an_instance">Vad är en instans\?</string>
<string name="logout">Logga ut</string> <string name="logout">Logga ut</string>
<string name="lbl_brightness">LJUSSTYRKA</string> <string name="lbl_brightness">LJUSSTYRKA</string>

View File

@ -9,7 +9,7 @@
<string name="theme_header">Тема</string> <string name="theme_header">Тема</string>
<string name="mention_notification">%1$s згадав(-ла) вас</string> <string name="mention_notification">%1$s згадав(-ла) вас</string>
<string name="description">Опис…</string> <string name="description">Опис…</string>
<string name="send">відправити</string> <string name="post">відправити</string>
<string name="lbl_brightness">Яскравість</string> <string name="lbl_brightness">Яскравість</string>
<string name="lbl_contrast">Контраст</string> <string name="lbl_contrast">Контраст</string>
<string name="lbl_saturation">Насичення</string> <string name="lbl_saturation">Насичення</string>

View File

@ -9,7 +9,7 @@
<string name="auth_failed">无法验证</string> <string name="auth_failed">无法验证</string>
<string name="token_error">获取验证令牌时出错</string> <string name="token_error">获取验证令牌时出错</string>
<string name="theme_header">主题</string> <string name="theme_header">主题</string>
<string name="send">发送</string> <string name="post">发送</string>
<string name="logout">注销</string> <string name="logout">注销</string>
<string name="app_name">PixelDroid</string> <string name="app_name">PixelDroid</string>
<string name="title_activity_settings2">配置</string> <string name="title_activity_settings2">配置</string>

View File

@ -44,8 +44,9 @@
<string name="upload_post_success">Post uploaded successfully</string> <string name="upload_post_success">Post uploaded successfully</string>
<string name="upload_post_error">Post upload failed</string> <string name="upload_post_error">Post upload failed</string>
<string name="description">Description…</string> <string name="description">Description…</string>
<string name="send">send</string> <string name="post">post</string>
<!-- Post edition --> <string name="add_photo">Add a photo</string>
<!-- Post editing -->
<string name="lbl_brightness">BRIGHTNESS</string> <string name="lbl_brightness">BRIGHTNESS</string>
<string name="lbl_contrast">CONTRAST</string> <string name="lbl_contrast">CONTRAST</string>
<string name="lbl_saturation">SATURATION</string> <string name="lbl_saturation">SATURATION</string>
@ -121,6 +122,19 @@
<string name="post_title">%1$s\'s post</string> <string name="post_title">%1$s\'s post</string>
<string name="followers_title">%1$s\'s followers</string> <string name="followers_title">%1$s\'s followers</string>
<string name="follows_title">%1$s\'s follows</string> <string name="follows_title">%1$s\'s follows</string>
<string name="search_empty_error">Search query can\'t be empty</string>
<string name="status_more_options">More options</string>
<string name="report">Report</string>
<string name="share_link">Share Link</string>
<string name="optional_report_comment">Optional message for mods/admins</string>
<string name="report_target">Report @%1$s\'s post</string>
<!-- Text on button, shown when report was successful. {gmd_check_circle} is an icon, position it as is appropriate in target language -->
<string name="reported">Reported {gmd_check_circle}</string>
<string name="report_error">Could not send report</string>
<string name="toolbar_title_edit">Edit</string>
<string name="profile_picture">Profile picture</string>
<string name="open_drawer_menu">Open drawer menu</string>
<string name="discover">DISCOVER</string>
</resources> </resources>

View File

@ -24,6 +24,5 @@
<style name="AppTheme.NoActionBar"> <style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item> <item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item> <item name="windowNoTitle">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style> </style>
</resources> </resources>

View File

@ -3,7 +3,7 @@
<PreferenceCategory app:title="@string/theme_header"> <PreferenceCategory app:title="@string/theme_header">
<ListPreference <ListPreference
app:defaultValue="@string/default_theme" app:defaultValue="default"
app:entries="@array/theme_entries" app:entries="@array/theme_entries"
app:entryValues="@array/theme_values" app:entryValues="@array/theme_values"
app:key="theme" app:key="theme"
@ -13,5 +13,9 @@
<Preference android:title="@string/about" <Preference android:title="@string/about"
android:key="about" android:key="about"
android:summary="@string/about_pixeldroid"/> android:summary="@string/about_pixeldroid">
<intent
android:targetPackage="com.h.pixeldroid"
android:targetClass="com.h.pixeldroid.AboutActivity"/>
</Preference>
</PreferenceScreen> </PreferenceScreen>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android" <searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/app_name" android:label="@string/app_name"
android:hint="Search" > android:hint="@string/search" >
</searchable> </searchable>