improved image crop

This commit is contained in:
Mariotaku Lee 2017-04-22 22:14:40 +08:00
parent 105046636f
commit 5b5b1e6102
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
43 changed files with 426 additions and 234 deletions

View File

@ -56,7 +56,7 @@ subprojects {
TwitterText : '1.14.3',
MediaViewerLibrary : '0.9.23',
MultiValueSwitch : '0.9.8',
PickNCrop : '0.9.25',
PickNCrop : '0.9.27',
AndroidGIFDrawable : '1.2.6',
KPreferences : '0.9.7',
Kovenant : '3.3.0',
@ -67,6 +67,7 @@ subprojects {
Glide : '3.7.0',
GlideOkHttp3 : '1.4.0',
GlideTransformations: '2.0.1',
AndroidImageCropper : '2.4.0',
]
}

View File

@ -19,10 +19,46 @@
package org.mariotaku.microblog.library.mastodon.model;
import org.mariotaku.restfu.http.SimpleValueMap;
import org.mariotaku.restfu.http.mime.Body;
/**
* Created by mariotaku on 2017/4/17.
*/
public class AccountUpdate extends SimpleValueMap {
public AccountUpdate displayName(String displayName) {
if (displayName != null) {
put("display_name", displayName);
} else {
put("display_name", null);
}
return this;
}
public AccountUpdate note(String note) {
if (note != null) {
put("note", note);
} else {
put("note", null);
}
return this;
}
public AccountUpdate avatar(Body avatar) {
if (avatar != null) {
put("avatar", avatar);
} else {
put("avatar", null);
}
return this;
}
public AccountUpdate header(Body header) {
if (header != null) {
put("header", header);
} else {
put("header", null);
}
return this;
}
}

View File

@ -161,7 +161,6 @@ dependencies {
compile "org.apache.james:apache-mime4j-core:${libVersions['Mime4J']}"
compile "org.apache.james:apache-mime4j-storage:${libVersions['Mime4J']}"
compile "com.bluelinelabs:logansquare:${libVersions['LoganSquare']}"
compile 'com.soundcloud.android:android-crop:1.0.1@aar'
compile "com.hannesdorfmann.parcelableplease:annotation:${libVersions['ParcelablePlease']}"
compile "com.github.mariotaku:PickNCrop:${libVersions['PickNCrop']}"
compile "com.github.mariotaku.RestFu:library:${libVersions['RestFu']}"
@ -178,6 +177,7 @@ dependencies {
compile "com.github.bumptech.glide:glide:${libVersions['Glide']}"
compile "com.github.bumptech.glide:okhttp3-integration:${libVersions['GlideOkHttp3']}@aar"
compile "jp.wasabeef:glide-transformations:${libVersions['GlideTransformations']}"
compile "com.theartofdev.edmodo:android-image-cropper:${libVersions['AndroidImageCropper']}"
compile "com.github.mariotaku.MediaViewerLibrary:base:${libVersions['MediaViewerLibrary']}"
compile "com.github.mariotaku.MediaViewerLibrary:subsample-image-view:${libVersions['MediaViewerLibrary']}"

View File

@ -528,8 +528,8 @@
android:theme="@style/Theme.Twidere"/>
<activity
android:name=".activity.ImageCropperActivity"
android:label="@string/crop_image"
android:theme="@style/Theme.Twidere.NoActionBar"
android:label="@string/title_crop_image"
android:theme="@style/Theme.Twidere"
android:windowSoftInputMode="adjustResize"/>
<activity
android:name=".activity.IncompatibleAlertActivity"

View File

@ -1,7 +1,7 @@
/*
* Twidere - Twitter client for Android
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
* Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -19,55 +19,164 @@
package org.mariotaku.twidere.activity
import android.content.Context
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.support.v7.widget.Toolbar
import com.soundcloud.android.crop.CropImageActivity
import org.mariotaku.kpreferences.get
import android.view.Menu
import android.view.MenuItem
import com.theartofdev.edmodo.cropper.CropImage
import com.theartofdev.edmodo.cropper.CropImageOptions
import com.theartofdev.edmodo.cropper.CropImageView
import kotlinx.android.synthetic.main.activity_crop_image.*
import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants
import org.mariotaku.twidere.TwidereConstants.SHARED_PREFERENCES_NAME
import org.mariotaku.twidere.activity.iface.IThemedActivity
import org.mariotaku.twidere.constant.themeBackgroundAlphaKey
import org.mariotaku.twidere.constant.themeBackgroundOptionKey
import org.mariotaku.twidere.constant.themeKey
import org.mariotaku.twidere.util.theme.getCurrentThemeResource
/**
* Created by mariotaku on 15/6/16.
* Built-in activity for image cropping.
* Use [CropImage.activity] to create a builder to start this activity.
*/
class ImageCropperActivity : CropImageActivity(), IThemedActivity {
class ImageCropperActivity : BaseActivity(), CropImageView.OnSetImageUriCompleteListener, CropImageView.OnCropImageCompleteListener {
// Data fields
override val currentThemeBackgroundAlpha by lazy { themeBackgroundAlpha }
override val currentThemeBackgroundOption by lazy { themeBackgroundOption }
/**
* Persist URI image to crop URI if specific permissions are required
*/
private val cropImageUri: Uri get() = intent.getParcelableExtra(CropImage.CROP_IMAGE_EXTRA_SOURCE)
private val preferences by lazy { getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE) }
/**
* the options that were set for the crop image
*/
private val options: CropImageOptions get() = intent.getParcelableExtra(CropImage.CROP_IMAGE_EXTRA_OPTIONS)
private var doneCancelBar: Toolbar? = null
/**
* Get Android uri to save the cropped image into.<br></br>
* Use the given in options or create a temp file.
*/
private val outputUri: Uri get() = options.outputUri
override fun onCreate(savedInstanceState: Bundle?) {
val prefs = getSharedPreferences(TwidereConstants.SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE)
val themeResource = getCurrentThemeResource(this, prefs[themeKey])
if (themeResource != 0) {
setTheme(themeResource)
}
@SuppressLint("NewApi")
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_crop_image)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
if (savedInstanceState == null) {
// no permissions required or already grunted, can start crop image activity
cropImageView.setImageUriAsync(cropImageUri)
}
}
override fun onContentChanged() {
super.onContentChanged()
doneCancelBar = findViewById(R.id.done_cancel_bar) as Toolbar
override fun onStart() {
super.onStart()
cropImageView.setOnSetImageUriCompleteListener(this)
cropImageView.setOnCropImageCompleteListener(this)
}
override fun setContentView(layoutResID: Int) {
super.setContentView(R.layout.activity_image_cropper)
override fun onStop() {
super.onStop()
cropImageView.setOnSetImageUriCompleteListener(null)
cropImageView.setOnCropImageCompleteListener(null)
}
override val themeBackgroundAlpha: Int
get() = preferences[themeBackgroundAlphaKey]
override fun onBackPressed() {
setResultCancel()
}
override val themeBackgroundOption: String
get() = preferences[themeBackgroundOptionKey]
override fun onSupportNavigateUp(): Boolean {
setResultCancel()
return true
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
super.onCreateOptionsMenu(menu)
menuInflater.inflate(R.menu.menu_crop_image, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.perform_crop -> {
cropImage()
return true
}
}
return super.onOptionsItemSelected(item)
}
override fun onSetImageUriComplete(view: CropImageView, uri: Uri, error: Exception?) {
if (error == null) {
if (options.initialCropWindowRectangle != null) {
cropImageView.cropRect = options.initialCropWindowRectangle
}
if (options.initialRotation > -1) {
cropImageView.rotatedDegrees = options.initialRotation
}
} else {
setResult(null, error, 1)
}
}
override fun onCropImageComplete(view: CropImageView, result: CropImageView.CropResult) {
setResult(result.uri, result.error, result.sampleSize)
}
//region: Private methods
/**
* Execute crop image and save the result to output uri.
*/
private fun cropImage() {
if (options.noOutputImage) {
setResult(null, null, 1)
} else {
val outputUri = outputUri
cropImageView.saveCroppedImageAsync(outputUri,
options.outputCompressFormat,
options.outputCompressQuality,
options.outputRequestWidth,
options.outputRequestHeight,
options.outputRequestSizeOptions)
}
}
/**
* Rotate the image in the crop image view.
*/
protected fun rotateImage(degrees: Int) {
cropImageView.rotateImage(degrees)
}
/**
* Result with cropped image data or error if failed.
*/
private fun setResult(uri: Uri?, error: Exception?, sampleSize: Int) {
val resultCode = if (error == null) Activity.RESULT_OK else CropImage.CROP_IMAGE_ACTIVITY_RESULT_ERROR_CODE
setResult(resultCode, getResultIntent(uri, error, sampleSize))
finish()
}
/**
* Cancel of cropping activity.
*/
private fun setResultCancel() {
setResult(Activity.RESULT_CANCELED)
finish()
}
/**
* Get intent instance to be used for the result of this activity.
*/
private fun getResultIntent(uri: Uri?, error: Exception?, sampleSize: Int): Intent {
val result = CropImage.ActivityResult(cropImageView.imageUri, uri, error,
cropImageView.cropPoints, cropImageView.cropRect, cropImageView.rotatedDegrees,
sampleSize)
val intent = Intent()
intent.putExtra(CropImage.CROP_IMAGE_EXTRA_RESULT, result)
return intent
}
//endregion
}

View File

@ -24,34 +24,36 @@ import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.os.Parcelable
import android.support.v4.app.DialogFragment
import android.support.v4.app.FragmentActivity
import android.support.v4.app.LoaderManager.LoaderCallbacks
import android.support.v4.content.Loader
import android.text.Editable
import android.text.TextUtils
import android.text.TextUtils.isEmpty
import android.text.TextWatcher
import android.view.*
import android.view.View.OnClickListener
import android.widget.Toast
import com.bumptech.glide.Glide
import com.twitter.Validator
import kotlinx.android.synthetic.main.fragment_user_profile_editor.*
import nl.komponents.kovenant.combine.and
import nl.komponents.kovenant.ui.promiseOnUi
import org.mariotaku.abstask.library.AbstractTask
import org.mariotaku.abstask.library.TaskStarter
import org.mariotaku.ktextension.dismissDialogFragment
import org.mariotaku.microblog.library.MicroBlog
import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.microblog.library.mastodon.Mastodon
import org.mariotaku.microblog.library.mastodon.model.AccountUpdate
import org.mariotaku.microblog.library.twitter.model.ProfileUpdate
import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants.*
import org.mariotaku.twidere.activity.ColorPickerDialogActivity
import org.mariotaku.twidere.activity.ThemedMediaPickerActivity
import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.extension.loadProfileBanner
import org.mariotaku.twidere.extension.loadProfileImage
import org.mariotaku.twidere.extension.model.api.toParcelable
import org.mariotaku.twidere.extension.model.api.mastodon.toParcelable
import org.mariotaku.twidere.extension.model.api.toParcelable
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
import org.mariotaku.twidere.loader.ParcelableUserLoader
@ -64,27 +66,18 @@ import org.mariotaku.twidere.task.*
import org.mariotaku.twidere.util.*
import org.mariotaku.twidere.view.iface.IExtendedView.OnSizeChangedListener
class UserProfileEditorFragment : BaseFragment(), OnSizeChangedListener, TextWatcher,
class UserProfileEditorFragment : BaseFragment(), OnSizeChangedListener,
OnClickListener, LoaderCallbacks<SingleResponse<ParcelableUser>>,
KeyboardShortcutsHandler.TakeAllKeyboardShortcut {
private var currentTask: AbstractTask<*, *, UserProfileEditorFragment>? = null
private val accountKey: UserKey
get() = arguments.getParcelable<UserKey>(EXTRA_ACCOUNT_KEY)
get() = arguments.getParcelable(EXTRA_ACCOUNT_KEY)
private var user: ParcelableUser? = null
private var account: AccountDetails? = null
private var userInfoLoaderInitialized: Boolean = false
private var getUserInfoCalled: Boolean = false
override fun beforeTextChanged(s: CharSequence, length: Int, start: Int, end: Int) {
}
override fun onTextChanged(s: CharSequence, length: Int, start: Int, end: Int) {
updateDoneButton()
}
override fun afterTextChanged(s: Editable) {
}
override fun onClick(view: View) {
val user = user
val task = currentTask
@ -137,10 +130,11 @@ class UserProfileEditorFragment : BaseFragment(), OnSizeChangedListener, TextWat
override fun onLoadFinished(loader: Loader<SingleResponse<ParcelableUser>>,
data: SingleResponse<ParcelableUser>) {
if (data.data != null && data.data.key != null) {
displayUser(data.data)
} else if (user == null) {
val user = data.data ?: this.user ?: run {
activity?.finish()
return
}
displayUser(user, data.extras.getParcelable(EXTRA_ACCOUNT))
}
override fun onLoaderReset(loader: Loader<SingleResponse<ParcelableUser>>) {
@ -180,10 +174,6 @@ class UserProfileEditorFragment : BaseFragment(), OnSizeChangedListener, TextWat
}
val lengthChecker = TwitterValidatorMETLengthChecker(Validator())
editName.addTextChangedListener(this)
editDescription.addTextChangedListener(this)
editLocation.addTextChangedListener(this)
editUrl.addTextChangedListener(this)
editDescription.setLengthChecker(lengthChecker)
@ -198,13 +188,15 @@ class UserProfileEditorFragment : BaseFragment(), OnSizeChangedListener, TextWat
setLinkColor.setOnClickListener(this)
setBackgroundColor.setOnClickListener(this)
if (savedInstanceState != null && savedInstanceState.getParcelable<Parcelable>(EXTRA_USER) != null) {
val user = savedInstanceState.getParcelable<ParcelableUser>(EXTRA_USER)!!
displayUser(user)
editName.setText(savedInstanceState.getString(EXTRA_NAME, user.name))
editLocation.setText(savedInstanceState.getString(EXTRA_LOCATION, user.location))
editDescription.setText(savedInstanceState.getString(EXTRA_DESCRIPTION, ParcelableUserUtils.getExpandedDescription(user)))
editUrl.setText(savedInstanceState.getString(EXTRA_URL, user.url_expanded))
val savedUser = savedInstanceState?.getParcelable<ParcelableUser?>(EXTRA_USER)
val savedAccount = savedInstanceState?.getParcelable<AccountDetails?>(EXTRA_ACCOUNT)
if (savedInstanceState != null && savedUser != null && savedAccount != null) {
displayUser(savedUser, savedAccount)
editName.setText(savedInstanceState.getString(EXTRA_NAME, savedUser.name))
editLocation.setText(savedInstanceState.getString(EXTRA_LOCATION, savedUser.location))
editDescription.setText(savedInstanceState.getString(EXTRA_DESCRIPTION,
ParcelableUserUtils.getExpandedDescription(savedUser)))
editUrl.setText(savedInstanceState.getString(EXTRA_URL, savedUser.url_expanded))
} else {
getUserInfo()
}
@ -213,6 +205,7 @@ class UserProfileEditorFragment : BaseFragment(), OnSizeChangedListener, TextWat
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putParcelable(EXTRA_USER, user)
outState.putParcelable(EXTRA_ACCOUNT, account)
outState.putString(EXTRA_NAME, ParseUtils.parseString(editName.text))
outState.putString(EXTRA_DESCRIPTION, ParseUtils.parseString(editDescription.text))
outState.putString(EXTRA_LOCATION, ParseUtils.parseString(editLocation.text))
@ -254,13 +247,11 @@ class UserProfileEditorFragment : BaseFragment(), OnSizeChangedListener, TextWat
REQUEST_PICK_LINK_COLOR -> {
if (resultCode == Activity.RESULT_OK) {
linkColor.color = data.getIntExtra(EXTRA_COLOR, 0)
updateDoneButton()
}
}
REQUEST_PICK_BACKGROUND_COLOR -> {
if (resultCode == Activity.RESULT_OK) {
backgroundColor.color = data.getIntExtra(EXTRA_COLOR, 0)
updateDoneButton()
}
}
}
@ -272,11 +263,12 @@ class UserProfileEditorFragment : BaseFragment(), OnSizeChangedListener, TextWat
}
}
private fun displayUser(user: ParcelableUser?) {
private fun displayUser(user: ParcelableUser?, account: AccountDetails?) {
if (!getUserInfoCalled) return
getUserInfoCalled = false
this.user = user
if (user != null) {
this.account = account
if (user != null && account != null) {
progressContainer.visibility = View.GONE
editProfileContent.visibility = View.VISIBLE
editName.setText(user.name)
@ -290,18 +282,44 @@ class UserProfileEditorFragment : BaseFragment(), OnSizeChangedListener, TextWat
linkColor.color = user.link_color
backgroundColor.color = user.background_color
if (USER_TYPE_TWITTER_COM == user.key.host) {
editProfileBanner.visibility = View.VISIBLE
editProfileBackground.visibility = View.GONE
} else {
editProfileBanner.visibility = View.GONE
editProfileBackground.visibility = View.VISIBLE
var canEditUrl = false
var canEditLocation = false
var canEditBanner = false
var canEditBackground = false
var canEditLinkColor = false
var canEditBackgroundColor = false
when (account.type) {
AccountType.TWITTER -> {
canEditUrl = true
canEditLocation = true
canEditBanner = true
canEditBackground = true
canEditLinkColor = true
canEditBackgroundColor = true
}
AccountType.MASTODON -> {
canEditBanner = true
}
AccountType.FANFOU -> {
canEditUrl = true
canEditLocation = true
canEditBackground = true
canEditLinkColor = true
canEditBackgroundColor = true
}
}
editProfileBanner.visibility = if (canEditBanner) View.VISIBLE else View.GONE
editProfileBackground.visibility = if (canEditBackground) View.VISIBLE else View.GONE
editUrl.visibility = if (canEditUrl) View.VISIBLE else View.GONE
editLocation.visibility = if (canEditLocation) View.VISIBLE else View.GONE
setLinkColor.visibility = if (canEditLinkColor) View.VISIBLE else View.GONE
setBackgroundColor.visibility = if (canEditBackgroundColor) View.VISIBLE else View.GONE
} else {
progressContainer.visibility = View.GONE
editProfileContent.visibility = View.GONE
}
updateDoneButton()
}
private fun getUserInfo() {
@ -340,10 +358,6 @@ class UserProfileEditorFragment : BaseFragment(), OnSizeChangedListener, TextWat
}
}
private fun updateDoneButton() {
}
internal class UpdateProfileTaskInternal(
fragment: UserProfileEditorFragment,
accountKey: UserKey,
@ -362,11 +376,42 @@ class UserProfileEditorFragment : BaseFragment(), OnSizeChangedListener, TextWat
}
override fun onExecute(account: AccountDetails, params: Any?): Pair<ParcelableUser, AccountDetails> {
val microBlog = account.newMicroBlogInstance(context = context, cls = MicroBlog::class.java)
val orig = this.original
if (orig != null && !orig.isProfileChanged()) {
return Pair(orig, account)
}
return Pair(when (account.type) {
AccountType.MASTODON -> updateMastodonProfile(account)
else -> updateMicroBlogProfile(account)
}, account)
}
override fun onSucceed(callback: UserProfileEditorFragment?, result: Pair<ParcelableUser, AccountDetails>) {
if (callback == null) return
promiseOnUi {
val context = this.callback?.context ?: return@promiseOnUi
val (user, account) = result
val task = UpdateAccountInfoTask(context)
task.params = Pair(account, user)
TaskStarter.execute(task)
} and callback.executeAfterFragmentResumed { fragment ->
fragment.childFragmentManager.dismissDialogFragment(DIALOG_FRAGMENT_TAG)
fragment.activity.finish()
}
}
override fun beforeExecute() {
super.beforeExecute()
callback?.executeAfterFragmentResumed { fragment ->
val df = ProgressDialogFragment.show(fragment.childFragmentManager, DIALOG_FRAGMENT_TAG)
df.isCancelable = false
}
}
private fun updateMicroBlogProfile(account: AccountDetails): ParcelableUser {
val microBlog = account.newMicroBlogInstance(context = context, cls = MicroBlog::class.java)
val profileUpdate = ProfileUpdate()
profileUpdate.name(HtmlEscapeHelper.escapeBasic(name))
profileUpdate.location(HtmlEscapeHelper.escapeBasic(location))
@ -374,10 +419,17 @@ class UserProfileEditorFragment : BaseFragment(), OnSizeChangedListener, TextWat
profileUpdate.url(url)
profileUpdate.linkColor(linkColor)
profileUpdate.backgroundColor(backgroundColor)
val user = microBlog.updateProfile(profileUpdate)
val profileImageSize = context.getString(R.string.profile_image_size)
return Pair(user.toParcelable(account, profileImageSize = profileImageSize), account)
return microBlog.updateProfile(profileUpdate).toParcelable(account,
profileImageSize = profileImageSize)
}
private fun updateMastodonProfile(account: AccountDetails): ParcelableUser {
val mastodon = account.newMicroBlogInstance(context = context, cls = Mastodon::class.java)
val accountUpdate = AccountUpdate()
accountUpdate.displayName(name)
accountUpdate.note(HtmlEscapeHelper.escapeBasic(description))
return mastodon.updateCredentials(accountUpdate).toParcelable(account)
}
private fun ParcelableUser.isProfileChanged(): Boolean {
@ -392,33 +444,6 @@ class UserProfileEditorFragment : BaseFragment(), OnSizeChangedListener, TextWat
return false
}
override fun afterExecute(callback: UserProfileEditorFragment?,
result: Pair<ParcelableUser, AccountDetails>?, exception: MicroBlogException?) {
super.afterExecute(callback, result, exception)
callback?.executeAfterFragmentResumed { fragment ->
fragment.childFragmentManager.dismissDialogFragment(DIALOG_FRAGMENT_TAG)
fragment.activity.finish()
}
}
override fun beforeExecute() {
super.beforeExecute()
callback?.executeAfterFragmentResumed { fragment ->
val df = ProgressDialogFragment.show(fragment.childFragmentManager, DIALOG_FRAGMENT_TAG)
df.isCancelable = false
}
}
override fun onSucceed(callback: UserProfileEditorFragment?, result: Pair<ParcelableUser, AccountDetails>) {
if (callback == null) return
val activity = callback.activity ?: return
val (user, account) = result
val task = UpdateAccountInfoTask(activity)
task.params = Pair(account, user)
TaskStarter.execute(task)
}
companion object {
private val DIALOG_FRAGMENT_TAG = "updating_user_profile"
@ -511,10 +536,8 @@ class UserProfileEditorFragment : BaseFragment(), OnSizeChangedListener, TextWat
override fun afterExecute(callback: UserProfileEditorFragment?, result: ParcelableUser?, exception: MicroBlogException?) {
super.afterExecute(callback, result, exception)
if (result != null) {
callback?.displayUser(result)
}
callback?.setUpdateState(false)
callback?.getUserInfo()
}
override fun beforeExecute() {

View File

@ -89,7 +89,7 @@ class ParcelableUserLoader(
return@firstOrNull false
} ?: return SingleResponse()
if (!omitIntentExtra && extras != null) {
val user = extras.getParcelable<ParcelableUser>(EXTRA_USER)
val user = extras.getParcelable<ParcelableUser?>(EXTRA_USER)
if (user != null) {
val values = ObjectCursor.valuesCreatorFrom(ParcelableUser::class.java).create(user)
resolver.insert(CachedUsers.CONTENT_URI, values)

View File

@ -29,8 +29,7 @@ class UpdateAccountInfoTask(
override fun doLongOperation(params: Pair<AccountDetails, ParcelableUser>) {
val resolver = context.contentResolver
val details = params.first
val user = params.second
val (details, user) = params
if (user.is_cache) {
return
}

View File

@ -5,9 +5,11 @@ import android.net.Uri
import android.widget.Toast
import org.mariotaku.microblog.library.MicroBlog
import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.microblog.library.mastodon.Mastodon
import org.mariotaku.microblog.library.mastodon.model.AccountUpdate
import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants.LOGTAG
import org.mariotaku.twidere.extension.model.api.toParcelable
import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.extension.model.api.mastodon.toParcelable
import org.mariotaku.twidere.extension.model.api.toParcelable
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
import org.mariotaku.twidere.model.AccountDetails
@ -32,24 +34,32 @@ open class UpdateProfileBannerImageTask<ResultHandler>(
private val profileImageSize = context.getString(R.string.profile_image_size)
override fun onExecute(account: AccountDetails, params: Any?): ParcelableUser {
val microBlog = account.newMicroBlogInstance(context, MicroBlog::class.java)
try {
UpdateStatusTask.getBodyFromMedia(context, imageUri, ParcelableMedia.Type.IMAGE,
return UpdateStatusTask.getBodyFromMedia(context, imageUri, ParcelableMedia.Type.IMAGE,
deleteImage, false, null, false, null).use {
microBlog.updateProfileBannerImage(it.body)
when (account.type) {
AccountType.MASTODON -> {
val mastodon = account.newMicroBlogInstance(context, Mastodon::class.java)
return@use mastodon.updateCredentials(AccountUpdate().header(it.body))
.toParcelable(account)
}
else -> {
val microBlog = account.newMicroBlogInstance(context, MicroBlog::class.java)
microBlog.updateProfileBannerImage(it.body)
// Wait for 5 seconds, see
// https://dev.twitter.com/docs/api/1.1/post/account/update_profile_image
Thread.sleep(5000L)
return@use microBlog.verifyCredentials().toParcelable(account,
profileImageSize = profileImageSize)
}
}
}
} catch (e: IOException) {
throw MicroBlogException(e)
}
// Wait for 5 seconds, see
// https://dev.twitter.com/docs/api/1.1/post/account/update_profile_image
try {
Thread.sleep(5000L)
} catch (e: InterruptedException) {
DebugLog.w(LOGTAG, tr = e)
DebugLog.w(tr = e)
throw MicroBlogException(e)
}
val user = microBlog.verifyCredentials()
return user.toParcelable(account, profileImageSize = profileImageSize)
}
override fun onSucceed(callback: ResultHandler?, result: ParcelableUser) {

View File

@ -5,8 +5,11 @@ import android.net.Uri
import android.widget.Toast
import org.mariotaku.microblog.library.MicroBlog
import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.microblog.library.mastodon.Mastodon
import org.mariotaku.microblog.library.mastodon.model.AccountUpdate
import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants
import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.extension.model.api.mastodon.toParcelable
import org.mariotaku.twidere.extension.model.api.toParcelable
import org.mariotaku.twidere.extension.model.newMicroBlogInstance
import org.mariotaku.twidere.model.AccountDetails
@ -31,28 +34,37 @@ open class UpdateProfileImageTask<ResultHandler>(
private val profileImageSize = context.getString(R.string.profile_image_size)
override fun onExecute(account: AccountDetails, params: Any?): ParcelableUser {
val microBlog = account.newMicroBlogInstance(context, MicroBlog::class.java)
try {
UpdateStatusTask.getBodyFromMedia(context, imageUri, ParcelableMedia.Type.IMAGE,
return UpdateStatusTask.getBodyFromMedia(context, imageUri, ParcelableMedia.Type.IMAGE,
deleteImage, false, null, false, null).use {
microBlog.updateProfileImage(it.body)
when (account.type) {
AccountType.MASTODON -> {
val mastodon = account.newMicroBlogInstance(context, Mastodon::class.java)
return@use mastodon.updateCredentials(AccountUpdate().avatar(it.body))
.toParcelable(account)
}
else -> {
val microBlog = account.newMicroBlogInstance(context, MicroBlog::class.java)
microBlog.updateProfileImage(it.body)
// Wait for 5 seconds, see
// https://dev.twitter.com/docs/api/1.1/post/account/update_profile_image
Thread.sleep(5000L)
return@use microBlog.verifyCredentials().toParcelable(account,
profileImageSize = profileImageSize)
}
}
}
} catch (e: IOException) {
throw MicroBlogException(e)
}
// Wait for 5 seconds, see
// https://dev.twitter.com/docs/api/1.1/post/account/update_profile_image
try {
Thread.sleep(5000L)
} catch (e: InterruptedException) {
DebugLog.w(TwidereConstants.LOGTAG, tr = e)
DebugLog.w(tr = e)
throw MicroBlogException(e)
}
val user = microBlog.verifyCredentials()
return user.toParcelable(account, profileImageSize = profileImageSize)
}
override fun onSucceed(callback: ResultHandler?, result: ParcelableUser) {
Toast.makeText(context, R.string.message_toast_profile_image_updated, Toast.LENGTH_SHORT).show()
Toast.makeText(context, R.string.message_toast_profile_image_updated, Toast.LENGTH_SHORT)
.show()
bus.post(ProfileUpdatedEvent(result))
}

View File

@ -355,7 +355,6 @@ class StatusViewHolder(private val adapter: IStatusesAdapter<*>, itemView: View)
mediaPreview.visibility = View.GONE
}
summaryView.text = status.extras?.summary_text
summaryView.hideIfEmpty()

View File

@ -191,7 +191,7 @@
<string name="count_label_retweets">من إعادات التغريد</string>
<string name="created_by">أنشأها <xliff:g id="created_by">%s</xliff:g></string>
<string name="created_list">أُنشأت القائمة <xliff:g id="list">%s</xliff:g>.</string>
<string name="crop_image">تقطيع الصورة</string>
<string name="title_crop_image">تقطيع الصورة</string>
<string name="current_status">التغريدة الحالية</string>
<string name="custom_host_mapping">تعيين مضيف مخصص</string>
<string name="custom_host_mapping_summary">استضافة رسم الخرائط مثل/إلخ/استضافات، ولكن لا تتطلب أية أذونات إضافية.</string>

View File

@ -254,7 +254,7 @@
<string name="count_label_retweets">Retuits</string>
<string name="created_by">Creáu por <xliff:g id="created_by">%s</xliff:g></string>
<string name="created_list">Creóse\'l llistáu «<xliff:g id="list">%s</xliff:g>».</string>
<string name="crop_image">Retayar imaxe</string>
<string name="title_crop_image">Retayar imaxe</string>
<string name="current_status">Tuit actual</string>
<string name="custom_host_mapping">Mapeáu d\'agospiu personalizáu</string>
<string name="custom_host_mapping_summary">Mapeáu d\'agospiu como /etc/hosts, pero nun rique permisu adicional dalu.</string>

View File

@ -169,7 +169,7 @@
<string name="count_label_retweets">Repiulades</string>
<string name="created_by">Creat per <xliff:g id="created_by">%s</xliff:g></string>
<string name="created_list">Heu creat la llista «<xliff:g id="list">%s</xliff:g>».</string>
<string name="crop_image">Retallar imatge</string>
<string name="title_crop_image">Retallar imatge</string>
<string name="current_status">Piulada actual</string>
<string name="custom_host_mapping">Associacions de servidors personalitzades</string>
<string name="custom_host_mapping_summary">Associa servidors com el fitxer /etc/hosts sense requerir permisos addicionals.</string>

View File

@ -107,7 +107,7 @@
<!-- [noun] Count label for retweets, e.g. (N retweets)-->
<string name="count_label_retweets">Retweety</string>
<string name="created_by">Vytvořil(a) <xliff:g id="created_by">%s </xliff:g></string>
<string name="crop_image">Oříznout obrázek</string>
<string name="title_crop_image">Oříznout obrázek</string>
<string name="current_status">Aktuální tweet</string>
<string name="dark_theme">Tmavý motiv</string>
<string name="default_account">Výchozí účet</string>

View File

@ -254,7 +254,7 @@
<string name="count_label_retweets">Retweets</string>
<string name="created_by">Erstellt von <xliff:g id="created_by">%s</xliff:g></string>
<string name="created_list">Liste \"<xliff:g id="list">%s</xliff:g>\" erstellt.</string>
<string name="crop_image">Bild zurechtschneiden</string>
<string name="title_crop_image">Bild zurechtschneiden</string>
<string name="current_status">Aktueller Tweet</string>
<string name="custom_host_mapping">Individuelles Host-Mapping</string>
<string name="custom_host_mapping_summary">Host-Mapping wie /ect/hosts, ohne zusätzlich erforderliche Rechte.</string>

View File

@ -255,7 +255,7 @@
<string name="count_label_retweets">Retweets</string>
<string name="created_by">Creado por <xliff:g id="created_by">%s</xliff:g></string>
<string name="created_list">La lista \"<xliff:g id="list">%s</xliff:g>\" ha sido creada.</string>
<string name="crop_image">Cortar imagen</string>
<string name="title_crop_image">Cortar imagen</string>
<string name="current_status">Tweet actual</string>
<string name="custom_host_mapping">Asignación de host personalizado</string>
<string name="custom_host_mapping_summary">Funciona como \"/etc/hosts\", pero no requiere permisos adicionales.</string>

View File

@ -203,7 +203,7 @@
<string name="count_label_retweets">بازتوییت‌ها</string>
<string name="created_by">ایجاد شده توسّط <xliff:g id="created_by">%s</xliff:g></string>
<string name="created_list">فهرست «<xliff:g id="list">%s</xliff:g>» را ساخت.</string>
<string name="crop_image">بریدن تصویر</string>
<string name="title_crop_image">بریدن تصویر</string>
<string name="current_status">توییت جاری</string>
<string name="custom_host_mapping">مسیردهی میزبان سفارشی</string>
<string name="custom_host_mapping_summary">مسیردهی میزبان مانند etc/hosts/، اما بدون نیاز به هرگونه دسترسی اضافی.</string>

View File

@ -173,7 +173,7 @@
<string name="count_label_retweets">Uudelleentwiittaukset</string>
<string name="created_by">Luoja <xliff:g id="created_by">%s</xliff:g></string>
<string name="created_list">Luotiin lista \"<xliff:g id="list">%s</xliff:g>\".</string>
<string name="crop_image">Rajaa kuva</string>
<string name="title_crop_image">Rajaa kuva</string>
<string name="current_status">Nykyinen twiitti</string>
<string name="custom_host_mapping">Mukautettu Host Mapping -määrittely</string>
<string name="custom_host_mapping_summary">Verkkonimet määritellään kuten /etc/hosts -tiedostossa, mutta ylimääräisiä oikeuksia ei tarvita.</string>

View File

@ -254,7 +254,7 @@
<string name="count_label_retweets">Retweets</string>
<string name="created_by">Créé par <xliff:g id="created_by">%s</xliff:g></string>
<string name="created_list">Liste \"<xliff:g id="list">%s</xliff:g>\" créée.</string>
<string name="crop_image">Rogner l\'image</string>
<string name="title_crop_image">Rogner l\'image</string>
<string name="current_status">Tweet en cours</string>
<string name="custom_host_mapping">Mappage d\'hôte personnalisée</string>
<string name="custom_host_mapping_summary">Mappage d\'hôte comme /etc/hosts, mais n\'exige pas de permissions supplémentaires.</string>

View File

@ -255,7 +255,7 @@
<string name="count_label_retweets">Rechouchíos</string>
<string name="created_by">Creado por <xliff:g id="created_by">%s</xliff:g></string>
<string name="created_list">Creada a lista \"<xliff:g id="list">%s</xliff:g>\".</string>
<string name="crop_image">Recortar imaxe</string>
<string name="title_crop_image">Recortar imaxe</string>
<string name="current_status">Chío actual</string>
<string name="custom_host_mapping">Personalizar host mapping</string>
<string name="custom_host_mapping_summary">Host mapping coma /etc/hosts, pero non require ningún permiso adicional.</string>

View File

@ -150,7 +150,7 @@
<string name="count_label_retweets">Retweetovi</string>
<string name="created_by">Stvorio <xliff:g id="created_by">%s</xliff:g></string>
<string name="created_list">Stvorena lista \"<xliff:g id="list">%s</xliff:g>\".</string>
<string name="crop_image">Obreži sliku</string>
<string name="title_crop_image">Obreži sliku</string>
<string name="custom_host_mapping">Mapiranje prilagođenog domaćina</string>
<string name="custom_host_mapping_summary">Mapiranje domaćina poput /npr/domaćini, ali ne zahtijeva dodatna dopuštenja.</string>
<string name="dark_theme">Tamna tema</string>

View File

@ -183,7 +183,7 @@
<string name="count_label_retweets">Retweet</string>
<string name="created_by">Létrehozta: <xliff:g id="created_by">%s</xliff:g></string>
<string name="created_list">\"<xliff:g id="list">%s</xliff:g>\" lista létrehozva.</string>
<string name="crop_image">Kép vágása</string>
<string name="title_crop_image">Kép vágása</string>
<string name="current_status">Jelenlegi tweet</string>
<string name="custom_host_mapping">Egyedi host hozzárendelés</string>
<string name="custom_host_mapping_summary">Host hozzárendelés, mint a /etc/hosts, de nem kell hozzá plusz jog.</string>

View File

@ -183,7 +183,7 @@
<string name="count_label_retweets">Retweets</string>
<string name="created_by">Dibuat oleh <xliff:g id="created_by">%s</xliff:g></string>
<string name="created_list">Membuat daftar \"<xliff:g id="list">%s</xliff:g>\".</string>
<string name="crop_image">Potong gambar</string>
<string name="title_crop_image">Potong gambar</string>
<string name="current_status">Twit sekarang</string>
<string name="custom_host_mapping">Kustom host mapping</string>
<string name="custom_host_mapping_summary">Host pemetaan sperti /etc/hosts, tanpa butuh permisi tambahan.</string>

View File

@ -168,7 +168,7 @@
<string name="count_label_retweets">Retweet</string>
<string name="created_by">Creato da <xliff:g id="created_by">%s</xliff:g></string>
<string name="created_list">Lista creata \"<xliff:g id="list">%s</xliff:g>\".</string>
<string name="crop_image">Ritaglia immagine</string>
<string name="title_crop_image">Ritaglia immagine</string>
<string name="current_status">Tweet corrente</string>
<string name="custom_host_mapping">Mappatura host personalizzata</string>
<string name="custom_host_mapping_summary">Mappa gli host come in /etc/hosts, ma non richiede permessi aggiuntivi.</string>

View File

@ -255,7 +255,7 @@
<string name="count_label_retweets">リツイート</string>
<string name="created_by"><xliff:g id="created_by">%s</xliff:g>により作成</string>
<string name="created_list">リスト\"<xliff:g id="list">%s</xliff:g>\"を作成しました。</string>
<string name="crop_image">トリミング</string>
<string name="title_crop_image">トリミング</string>
<string name="current_status">現在のツイート</string>
<string name="custom_host_mapping">ホストマッピングの設定</string>
<string name="custom_host_mapping_summary">rootedでなくても/etc/hostsのようにホストマッピングができます。</string>

View File

@ -217,7 +217,7 @@
<string name="count_label_retweets">리트윗</string>
<string name="created_by"><xliff:g id="created_by">%s</xliff:g>님이 만듦</string>
<string name="created_list">\"<xliff:g id="list">%s</xliff:g>\" 리스트를 만들었습니다.</string>
<string name="crop_image">이미지 자르기</string>
<string name="title_crop_image">이미지 자르기</string>
<string name="current_status">현재 트윗</string>
<string name="custom_host_mapping">커스텀 호스트 매핑</string>
<string name="custom_host_mapping_summary">/etc/hosts 에 지정하는 것처럼 호스트를 매핑합니다. 다른 권한은 전혀 필요 없습니다.</string>

View File

@ -168,7 +168,7 @@
<string name="count_label_retweets">Retweets</string>
<string name="created_by">Gemaakt door <xliff:g id="created_by">%s</xliff:g></string>
<string name="created_list">\'\'<xliff:g id="list">%s</xliff:g>\'\' lijst aangemaakt.</string>
<string name="crop_image">Afbeelding bijsnijden</string>
<string name="title_crop_image">Afbeelding bijsnijden</string>
<string name="current_status">Huidige tweet</string>
<string name="custom_host_mapping">Aangepaste host toewijzingen</string>
<string name="custom_host_mapping_summary">Host toewijzingen zoals /etc/hosts, maar heeft geen aanvullende rechten nodig.</string>

View File

@ -160,7 +160,7 @@
<string name="count_label_retweets">Retweets</string>
<string name="created_by">Opprettet av <xliff:g id="created_by">%s</xliff:g></string>
<string name="created_list">Opprettet listen «<xliff:g id="list">%s</xliff:g>».</string>
<string name="crop_image">Beskjær bilde</string>
<string name="title_crop_image">Beskjær bilde</string>
<string name="current_status">Gjeldende tweet</string>
<string name="custom_host_mapping">Tilpasset vertskobling</string>
<string name="custom_host_mapping_summary">Vertskobling som /etc/hosts, men krever ingen ekstra tillatelser.</string>

View File

@ -196,7 +196,7 @@
<string name="count_label_retweets">Retweets</string>
<string name="created_by">Criada por <xliff:g id="created_by">%s</xliff:g></string>
<string name="created_list">A lista \"<xliff:g id="list">%s</xliff:g>\" foi criada.</string>
<string name="crop_image">Cortar imagem</string>
<string name="title_crop_image">Cortar imagem</string>
<string name="current_status">Tweet atual</string>
<string name="custom_host_mapping">Mapeamento de hosts personalizado</string>
<string name="custom_host_mapping_summary">Mapear hosts tais como /etc/hosts, mas não requer outras permissões adicionais.</string>

View File

@ -189,7 +189,7 @@
<string name="count_label_retweets">Ретвиты</string>
<string name="created_by">Создано пользователем <xliff:g id="created_by">%s</xliff:g></string>
<string name="created_list">Созданный список \"<xliff:g id="list">%s</xliff:g>\".</string>
<string name="crop_image">Обрезать изображение</string>
<string name="title_crop_image">Обрезать изображение</string>
<string name="current_status">Текущий твит</string>
<string name="custom_host_mapping">Перенаправление узлов</string>
<string name="custom_host_mapping_summary">Перенаправление узлов, как с помощью /etc/hosts, но не требующее дополнительных разрешений.</string>

View File

@ -255,7 +255,7 @@
<string name="count_label_retweets">Retweets</string>
<string name="created_by">Skapad av <xliff:g id="created_by">%s</xliff:g></string>
<string name="created_list">Skapade listan \"<xliff:g id="list">%s</xliff:g>\".</string>
<string name="crop_image">Beskär bild</string>
<string name="title_crop_image">Beskär bild</string>
<string name="current_status">Nuvarande tweet</string>
<string name="custom_host_mapping">Anpassad värdmappning</string>
<string name="custom_host_mapping_summary">Värdmappning som exempelvis /etc/hosts, men som inte kräver ytterligare rättigheter.</string>

View File

@ -255,7 +255,7 @@
<string name="count_label_retweets">รีทวีต</string>
<string name="created_by">สร้างโดย <xliff:g id="created_by">%s</xliff:g></string>
<string name="created_list">สร้างลิสต์ \"<xliff:g id="list">%s</xliff:g>\" แล้ว</string>
<string name="crop_image">ตัดรูปภาพ</string>
<string name="title_crop_image">ตัดรูปภาพ</string>
<string name="current_status">ทวีตปัจจุบัน</string>
<string name="custom_host_mapping">กำหนดที่อยู่ host เอง</string>
<string name="custom_host_mapping_summary">กำหนดที่อยู่ host เอง (เหมือนในไฟล์ /etc/hosts) ซึ่งการกระทำนี้ไม่ต้องการสิทธิ์ใดๆ เพิ่มเติม</string>

View File

@ -171,7 +171,7 @@
<string name="count_label_retweets">Retweetler</string>
<string name="created_by"><xliff:g id="created_by">%s</xliff:g> tarafından oluşturuldu</string>
<string name="created_list"><xliff:g id="list">%s</xliff:g> listesi oluşturuldu.</string>
<string name="crop_image">Görüntüyü kırp</string>
<string name="title_crop_image">Görüntüyü kırp</string>
<string name="current_status">Güncel tweet</string>
<string name="custom_host_mapping">Özel sunucu haritalama</string>
<string name="custom_host_mapping_summary">/etc/hosts şeklinde sunucu haritalama, ancak ekstra izin gerektirmez.</string>

View File

@ -172,7 +172,7 @@
<string name="count_label_retweets">Ретвіти</string>
<string name="created_by">Створено <xliff:g id="created_by">%s</xliff:g></string>
<string name="created_list">Створено список \"<xliff:g id="list">%s</xliff:g>\".</string>
<string name="crop_image">Обрізати зображення</string>
<string name="title_crop_image">Обрізати зображення</string>
<string name="current_status">Поточний твіт</string>
<string name="custom_host_mapping">Перенаправлення вузлів</string>
<string name="custom_host_mapping_summary">Перенаправлення вузлів, як за допомогою /etc/hosts, але не потребує додаткових дозволів.</string>

View File

@ -255,7 +255,7 @@
<string name="count_label_retweets">转推</string>
<string name="created_by"><xliff:g id="created_by">%s</xliff:g> 创建</string>
<string name="created_list">创建了列表 “<xliff:g id="list">%s</xliff:g></string>
<string name="crop_image">裁剪图像</string>
<string name="title_crop_image">裁剪图像</string>
<string name="current_status">当前推文</string>
<string name="custom_host_mapping">自定义主机映射</string>
<string name="custom_host_mapping_summary">如同 /etc/hosts 的主机映射,但不需要任何额外的权限</string>

View File

@ -255,7 +255,7 @@
<string name="count_label_retweets">轉推</string>
<string name="created_by"><xliff:g id="created_by">%s</xliff:g>創建</string>
<string name="created_list">建立了列表 \"<xliff:g id="list">%s</xliff:g>\"</string>
<string name="crop_image">裁剪圖片</string>
<string name="title_crop_image">裁剪圖片</string>
<string name="current_status">當前推文</string>
<string name="custom_host_mapping">自訂主機對應</string>
<string name="custom_host_mapping_summary">如同/etc/hosts的主機對應但不需要任何額外的權限</string>

View File

@ -255,7 +255,7 @@
<string name="count_label_retweets">轉推</string>
<string name="created_by"><xliff:g id="created_by">%s</xliff:g>創建</string>
<string name="created_list">建立了列表 \"<xliff:g id="list">%s</xliff:g>\"</string>
<string name="crop_image">裁剪圖片</string>
<string name="title_crop_image">裁剪圖片</string>
<string name="current_status">當前推文</string>
<string name="custom_host_mapping">自訂主機對應</string>
<string name="custom_host_mapping_summary">如同/etc/hosts的主機對應但不需要任何額外的權限</string>

View File

@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Twidere - Twitter client for Android
~
~ Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.theartofdev.edmodo.cropper.CropImageView
android:id="@+id/cropImageView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>

View File

@ -1,55 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Twidere - Twitter client for Android
~
~ Copyright (C) 2012-2015 Mariotaku Lee <mariotaku.lee@gmail.com>
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<RelativeLayout
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">
<com.soundcloud.android.crop.CropImageView
android:id="@+id/crop_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/done_cancel_bar"
android:background="@drawable/crop__texture"/>
<android.support.v7.widget.Toolbar
android:id="@+id/done_cancel_bar"
style="?attr/actionBarStyle"
android:layout_width="match_parent"
android:layout_height="?actionBarSize"
android:layout_alignParentTop="true"
android:touchscreenBlocksFocus="true"
app:contentInsetEnd="0dp"
app:contentInsetStart="0dp"
tools:ignore="UnusedAttribute">
<include layout="@layout/layout_image_cropper_done_cancel"/>
</android.support.v7.widget.Toolbar>
<View
android:id="@+id/window_overlay"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/done_cancel_bar"
android:background="?android:windowContentOverlay"/>
</RelativeLayout>

View File

@ -25,7 +25,7 @@
android:orientation="horizontal">
<org.mariotaku.twidere.view.FixedTextView
android:id="@+id/btn_cancel"
android:id="@+id/btnCancel"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
@ -37,7 +37,7 @@
android:textStyle="bold"/>
<org.mariotaku.twidere.view.FixedTextView
android:id="@+id/btn_done"
android:id="@+id/btnDone"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Twidere - Twitter client for Android
~
~ Copyright (C) 2012-2017 Mariotaku Lee <mariotaku.lee@gmail.com>
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/perform_crop"
android:icon="@drawable/ic_action_confirm"
android:title="@android:string/ok"
app:showAsAction="always"/>
</menu>

View File

@ -297,8 +297,6 @@
<string name="created_by">Created by <xliff:g id="created_by">%s</xliff:g></string>
<string name="created_list">Created list \"<xliff:g id="list">%s</xliff:g>\".</string>
<string name="crop_image">Crop image</string>
<string name="current_status">Current tweet</string>
<string name="custom_host_mapping">Custom host mapping</string>
@ -1197,6 +1195,7 @@
<string name="title_buffer_settings">Buffer settings</string>
<string name="title_compose">Compose</string>
<string name="title_conversation">Conversation</string>
<string name="title_crop_image">Crop image</string>
<string name="title_dialog_sync_connect_to">Connect to…</string>
<string name="title_direct_messages">Messages</string>
<string name="title_direct_messages_conversation_info">Conversation info</string>