code cleanup

This commit is contained in:
Mariotaku Lee 2017-04-17 10:44:58 +08:00
parent 933a3f8e1a
commit 775ef69952
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
14 changed files with 208 additions and 128 deletions

View File

@ -54,4 +54,11 @@ public class BitmapUtils {
BitmapFactory.decodeFile(image.getPath(), o);
return o.outMimeType;
}
public static int calculateInSampleSize(final int width, final int height, final int preferredWidth,
final int preferredHeight) {
if (preferredHeight > height && preferredWidth > width) return 1;
final int result = Math.round(Math.max(width, height) / (float) Math.max(preferredWidth, preferredHeight));
return Math.max(1, result);
}
}

View File

@ -55,20 +55,15 @@ import android.support.v4.net.ConnectivityManagerCompat;
import android.support.v4.view.GravityCompat;
import android.support.v4.view.accessibility.AccessibilityEventCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.view.menu.MenuBuilder;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.Display;
import android.view.Gravity;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.View;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.widget.Toast;
@ -99,7 +94,6 @@ import org.mariotaku.twidere.view.TabPagerIndicator;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
@ -159,13 +153,6 @@ public final class Utils implements Constants {
accessibilityManager.sendAccessibilityEvent(event);
}
public static int calculateInSampleSize(final int width, final int height, final int preferredWidth,
final int preferredHeight) {
if (preferredHeight > height && preferredWidth > width) return 1;
final int result = Math.round(Math.max(width, height) / (float) Math.max(preferredWidth, preferredHeight));
return Math.max(1, result);
}
public static boolean closeSilently(final Closeable c) {
if (c == null) return false;
try {
@ -800,18 +787,6 @@ public final class Utils implements Constants {
showErrorMessage(context, message, long_message);
}
public static void startStatusShareChooser(final Context context, final ParcelableStatus status) {
final Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");
final String name = status.user_name, screenName = status.user_screen_name;
final String timeString = formatToLongTimeString(context, status.timestamp);
final String subject = context.getString(R.string.status_share_subject_format_with_time, name, screenName, timeString);
intent.putExtra(Intent.EXTRA_SUBJECT, subject);
intent.putExtra(Intent.EXTRA_TEXT, status.text_plain);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
context.startActivity(Intent.createChooser(intent, context.getString(R.string.action_share)));
}
public static String trimLineBreak(final String orig) {
if (orig == null) return null;
return orig.replaceAll("\\n+", "\n");
@ -863,28 +838,6 @@ public final class Utils implements Constants {
return 0;
}
public static <T> Object findFieldOfTypes(T obj, Class<? extends T> cls, Class<?>... checkTypes) {
labelField:
for (Field field : cls.getDeclaredFields()) {
field.setAccessible(true);
final Object fieldObj;
try {
fieldObj = field.get(obj);
} catch (Exception ignore) {
continue;
}
if (fieldObj != null) {
final Class<?> type = fieldObj.getClass();
for (Class<?> checkType : checkTypes) {
if (!checkType.isAssignableFrom(type)) continue labelField;
}
return fieldObj;
}
}
return null;
}
public static int getNotificationId(int baseId, @Nullable UserKey accountKey) {
int result = baseId;
result = 31 * result + (accountKey != null ? accountKey.hashCode() : 0);
@ -962,38 +915,4 @@ public final class Utils implements Constants {
return location;
}
public static boolean checkDeviceCompatible() {
try {
Menu.class.isAssignableFrom(MenuBuilder.class);
} catch (Error e) {
Analyzer.Companion.logException(e);
return false;
}
return true;
}
/**
* Detect whether screen minimum width is not smaller than 600dp, regardless split screen mode
*/
public static boolean isDeviceTablet(@NonNull Context context) {
DisplayMetrics metrics = new DisplayMetrics();
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
final Display defaultDisplay = wm.getDefaultDisplay();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
defaultDisplay.getMetrics(metrics);
} else {
defaultDisplay.getRealMetrics(metrics);
}
final float mw = Math.min(metrics.widthPixels / metrics.density, metrics.heightPixels / metrics.density);
return mw >= 600;
}
/*
* May return false on tablets when using split window
*/
public static boolean isScreenTablet(@NonNull Context context) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return metrics.widthPixels / metrics.density >= 600;
}
}

View File

@ -26,8 +26,9 @@ import android.util.AttributeSet
import android.view.Menu
import android.view.View
import android.widget.ImageView
import org.mariotaku.twidere.extension.findFieldByTypes
import org.mariotaku.twidere.extension.get
import org.mariotaku.twidere.util.ThemeUtils
import org.mariotaku.twidere.util.Utils
/**
* Created by mariotaku on 15/1/16.
@ -39,10 +40,18 @@ class TwidereToolbar(context: Context, attrs: AttributeSet?) : Toolbar(context,
override fun getMenu(): Menu {
val menu = super.getMenu()
ThemeUtils.setActionBarOverflowColor(this, itemColor)
val menuView = Utils.findFieldOfTypes(this, Toolbar::class.java,
ActionMenuView::class.java) as? ActionMenuView ?: return menu
val presenter = Utils.findFieldOfTypes(menuView, ActionMenuView::class.java,
ActionMenuPresenter::class.java) as? ActionMenuPresenter ?: return menu
val menuViewField = try {
Toolbar::class.java.findFieldByTypes(ActionMenuView::class.java)
} catch (e: Exception) {
null
} ?: return menu
val menuView = this[menuViewField] as? ActionMenuView ?: return menu
val presenterField = try {
ActionMenuView::class.java.findFieldByTypes(ActionMenuPresenter::class.java)
} catch (e: Exception) {
null
} ?: return menu
val presenter = menuView[presenterField] as? ActionMenuPresenter ?: return menu
setActionBarOverflowColor(presenter, itemColor)
return menu
}
@ -62,8 +71,12 @@ class TwidereToolbar(context: Context, attrs: AttributeSet?) : Toolbar(context,
companion object {
private fun setActionBarOverflowColor(presenter: ActionMenuPresenter, itemColor: Int) {
val view = Utils.findFieldOfTypes(presenter, ActionMenuPresenter::class.java,
ActionMenuView.ActionMenuChildView::class.java, View::class.java) as? ImageView ?: return
val viewField = try {
ActionMenuPresenter::class.java.findFieldByTypes(ActionMenuView.ActionMenuChildView::class.java, View::class.java)
} catch (e: Exception) {
null
} ?: return
val view = presenter[viewField] as? ImageView ?: return
view.setColorFilter(itemColor, PorterDuff.Mode.SRC_ATOP)
}
}

View File

@ -868,7 +868,7 @@ class HomeActivity : BaseActivity(), OnClickListener, OnPageChangeListener, Supp
fun hasMultiColumns(): Boolean {
if (!Utils.isDeviceTablet(this) || !Utils.isScreenTablet(this)) return false
if (!DeviceUtils.isDeviceTablet(this) || !DeviceUtils.isScreenTablet(this)) return false
if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
return preferences.getBoolean("multi_column_tabs_landscape", resources.getBoolean(R.bool.default_multi_column_tabs_land))
}

View File

@ -28,6 +28,7 @@ import org.mariotaku.twidere.R
import org.mariotaku.twidere.constant.IntentConstants.EXTRA_INTENT
import org.mariotaku.twidere.extension.model.hasInvalidAccount
import org.mariotaku.twidere.model.util.AccountUtils
import org.mariotaku.twidere.util.DeviceUtils
import org.mariotaku.twidere.util.StrictModeUtils
import org.mariotaku.twidere.util.Utils
@ -40,7 +41,7 @@ open class MainActivity : BaseActivity() {
}
super.onCreate(savedInstanceState)
val am = AccountManager.get(this)
if (!Utils.checkDeviceCompatible()) {
if (!DeviceUtils.checkCompatibility()) {
startActivity(Intent(this, IncompatibleAlertActivity::class.java))
} else if (!AccountUtils.hasAccountPermission(am)) {
Toast.makeText(this, R.string.message_toast_no_account_permission, Toast.LENGTH_SHORT).show()

View File

@ -48,9 +48,9 @@ import org.mariotaku.twidere.constant.KeyboardShortcutConstants.ACTION_NAVIGATIO
import org.mariotaku.twidere.constant.KeyboardShortcutConstants.CONTEXT_TAG_NAVIGATION
import org.mariotaku.twidere.extension.applyTheme
import org.mariotaku.twidere.fragment.*
import org.mariotaku.twidere.util.DeviceUtils
import org.mariotaku.twidere.util.KeyboardShortcutsHandler
import org.mariotaku.twidere.util.ThemeUtils
import org.mariotaku.twidere.util.Utils
import java.util.*
class SettingsActivity : BaseActivity(), OnItemClickListener, OnPreferenceStartFragmentCallback {
@ -199,7 +199,7 @@ class SettingsActivity : BaseActivity(), OnItemClickListener, OnPreferenceStartF
R.xml.preferences_theme)
entriesAdapter.addPreference("cards", R.drawable.ic_action_card, getString(R.string.cards),
R.xml.preferences_cards)
if (Utils.isDeviceTablet(this)) {
if (DeviceUtils.isDeviceTablet(this)) {
entriesAdapter.addPreference("tablet_mode", R.drawable.ic_action_tablet, getString(R.string.preference_title_tablet_mode),
R.xml.preferences_tablet_mode)
}

View File

@ -0,0 +1,32 @@
/*
* 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/>.
*/
package org.mariotaku.twidere.extension
import java.lang.reflect.Field
/**
* Created by mariotaku on 2017/4/17.
*/
fun Class<*>.findFieldByTypes(vararg checkTypes: Class<*>): Field? {
return declaredFields.firstOrNull { field ->
checkTypes.all { it.isAssignableFrom(field.type) }
}
}

View File

@ -0,0 +1,36 @@
/*
* 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/>.
*/
package org.mariotaku.twidere.extension
import java.lang.reflect.Field
/**
* Created by mariotaku on 2017/4/17.
*/
operator fun Any.get(field: Field): Any? {
val accessible = field.isAccessible
try {
field.isAccessible = true
return field[this]
} finally {
field.isAccessible = accessible
}
}

View File

@ -16,7 +16,7 @@ fun ParcelableMediaUpdate.getMimeType(resolver: ContentResolver): String? {
ParcelableMedia.Type.IMAGE -> {
val o = BitmapFactory.Options()
o.inJustDecodeBounds = true
BitmapFactoryUtils.decodeUri(resolver, uri, null, o)
BitmapFactoryUtils.decodeUri(resolver, uri, opts = o)
return o.outMimeType
}
else -> return null

View File

@ -688,7 +688,7 @@ class UpdateStatusTask(
this.deleteAlways = deleteAlways
}
} finally {
Utils.closeSilently(body)
body?.close()
}
body?.deleteOnSuccess?.addAllTo(deleteOnSuccess)
body?.deleteAlways?.addAllTo(deleteAlways)
@ -811,7 +811,7 @@ class UpdateStatusTask(
var mediaType = defaultType
val o = BitmapFactory.Options()
o.inJustDecodeBounds = true
BitmapFactoryUtils.decodeUri(resolver, mediaUri, null, o)
BitmapFactoryUtils.decodeUri(resolver, mediaUri, opts = o)
// Try to use decoded media type
if (o.outMimeType != null) {
mediaType = o.outMimeType
@ -823,7 +823,7 @@ class UpdateStatusTask(
if (imageLimit != null) {
if (imageLimit.checkGeomentry(o.outWidth, o.outHeight)) return null
o.inSampleSize = Utils.calculateInSampleSize(o.outWidth, o.outHeight,
o.inSampleSize = BitmapUtils.calculateInSampleSize(o.outWidth, o.outHeight,
imageLimit.maxWidth, imageLimit.maxHeight)
}
o.inJustDecodeBounds = false

View File

@ -6,7 +6,6 @@ import android.graphics.BitmapFactory
import android.graphics.Rect
import android.net.Uri
import java.io.IOException
import java.io.InputStream
/**
* Created by mariotaku on 16/7/31.
@ -14,16 +13,10 @@ import java.io.InputStream
object BitmapFactoryUtils {
@Throws(IOException::class)
fun decodeUri(contentResolver: ContentResolver, uri: Uri, outPadding: Rect?,
opts: BitmapFactory.Options?, close: Boolean = true): Bitmap? {
var st: InputStream? = null
try {
st = contentResolver.openInputStream(uri)
return BitmapFactory.decodeStream(st, outPadding, opts)
} finally {
if (close) {
Utils.closeSilently(st)
}
fun decodeUri(contentResolver: ContentResolver, uri: Uri, outPadding: Rect? = null,
opts: BitmapFactory.Options? = null): Bitmap? {
return contentResolver.openInputStream(uri).use {
BitmapFactory.decodeStream(it, outPadding, opts)
}
}

View File

@ -0,0 +1,69 @@
/*
* 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/>.
*/
package org.mariotaku.twidere.util
import android.content.Context
import android.os.Build
import android.support.v7.view.menu.MenuBuilder
import android.util.DisplayMetrics
import android.view.Menu
import android.view.WindowManager
/**
* Created by mariotaku on 2017/4/17.
*/
object DeviceUtils {
fun checkCompatibility(): Boolean {
try {
Menu::class.java.isAssignableFrom(MenuBuilder::class.java)
} catch (e: Error) {
Analyzer.logException(e)
return false
}
return true
}
/**
* Detect whether screen minimum width is not smaller than 600dp, regardless split screen mode
*/
fun isDeviceTablet(context: Context): Boolean {
val metrics = DisplayMetrics()
val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
val defaultDisplay = wm.defaultDisplay
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
defaultDisplay.getMetrics(metrics)
} else {
defaultDisplay.getRealMetrics(metrics)
}
val mw = Math.min(metrics.widthPixels / metrics.density, metrics.heightPixels / metrics.density)
return mw >= 600
}
/*
* May return false on tablets when using split window
*/
fun isScreenTablet(context: Context): Boolean {
val metrics = context.resources.displayMetrics
return metrics.widthPixels / metrics.density >= 600
}
}

View File

@ -108,9 +108,7 @@ class OAuthPasswordAuthenticator(
@Throws(IOException::class, LoginVerificationException::class)
private fun getVerificationData(authorizeResponseData: AuthorizeResponseData,
challengeResponse: String?): AuthorizeRequestData {
var response: HttpResponse? = null
try {
val data = AuthorizeRequestData()
val params = MultiValueMap<String>()
val verification = authorizeResponseData.challenge!!
params.add("authenticity_token", verification.authenticityToken)
@ -132,16 +130,16 @@ class OAuthPasswordAuthenticator(
authorizeResultBuilder.url(endpoint.construct("/account/login_verification"))
authorizeResultBuilder.headers(requestHeaders)
authorizeResultBuilder.body(authorizationResultBody)
response = client.newCall(authorizeResultBuilder.build()).execute()
parseAuthorizeRequestData(response, data)
if (TextUtils.isEmpty(data.authenticityToken)) {
throw LoginVerificationException()
return client.newCall(authorizeResultBuilder.build()).execute().use {
val data = AuthorizeRequestData()
parseAuthorizeRequestData(it, data)
if (data.authenticityToken.isNullOrEmpty()) {
throw LoginVerificationException()
}
return@use data
}
return data
} catch (e: ParseException) {
throw LoginVerificationException("Login verification challenge failed", e)
} finally {
Utils.closeSilently(response)
}
}
@ -188,7 +186,6 @@ class OAuthPasswordAuthenticator(
private fun getAuthorizeResponseData(requestToken: OAuthToken,
authorizeRequestData: AuthorizeRequestData,
username: String, password: String): AuthorizeResponseData {
var response: HttpResponse? = null
try {
val data = AuthorizeResponseData()
val params = MultiValueMap<String>()
@ -209,7 +206,6 @@ class OAuthPasswordAuthenticator(
authorizeResultBuilder.url(endpoint.construct("/oauth/authorize"))
authorizeResultBuilder.headers(requestHeaders)
authorizeResultBuilder.body(authorizationResultBody)
response = client.newCall(authorizeResultBuilder.build()).execute()
val handler = object : AbstractSimpleMarkupHandler() {
internal var isOAuthPinDivOpened: Boolean = false
internal var isChallengeFormOpened: Boolean = false
@ -297,18 +293,17 @@ class OAuthPasswordAuthenticator(
}
}
}
PARSER.parse(SimpleBody.reader(response!!.body), handler)
client.newCall(authorizeResultBuilder.build()).execute().use {
PARSER.parse(SimpleBody.reader(it.body), handler)
}
return data
} catch (e: ParseException) {
throw AuthenticationException("Malformed HTML", e)
} finally {
Utils.closeSilently(response)
}
}
@Throws(IOException::class, AuthenticationException::class)
private fun getAuthorizeRequestData(requestToken: OAuthToken): AuthorizeRequestData {
var response: HttpResponse? = null
try {
val data = AuthorizeRequestData()
val authorizePageBuilder = HttpRequest.Builder()
@ -321,16 +316,15 @@ class OAuthPasswordAuthenticator(
requestHeaders.add("User-Agent", userAgent)
authorizePageBuilder.headers(requestHeaders)
val authorizePageRequest = authorizePageBuilder.build()
response = client.newCall(authorizePageRequest).execute()
parseAuthorizeRequestData(response, data)
if (TextUtils.isEmpty(data.authenticityToken)) {
client.newCall(authorizePageRequest).execute().use {
parseAuthorizeRequestData(it, data)
}
if (data.authenticityToken.isNullOrEmpty()) {
throw AuthenticationException()
}
return data
} catch (e: ParseException) {
throw AuthenticationException("Malformed HTML", e)
} finally {
Utils.closeSilently(response)
}
}

View File

@ -49,12 +49,27 @@ abstract class TimelineSyncManager(val context: Context) {
}
fun blockingGetPosition(@ReadPositionTag positionTag: String, currentTag: String?): Long {
val position = fetchPosition(positionTag, currentTag)
synchronized(cachedPositions) {
cachedPositions[TimelineKey(positionTag, currentTag)] = position
}
return position
}
fun peekPosition(@ReadPositionTag positionTag: String, currentTag: String?): Long {
synchronized(cachedPositions) {
return cachedPositions[TimelineKey(positionTag, currentTag)] ?: -1
}
}
@UiThread
protected abstract fun performSync(data: Array<PositionData>)
@WorkerThread
@Throws(IOException::class)
abstract fun blockingGetPosition(@ReadPositionTag positionTag: String, currentTag: String?): Long
protected abstract fun fetchPosition(@ReadPositionTag positionTag: String, currentTag: String?): Long
data class TimelineKey(val positionTag: String, val currentTag: String?)
data class PositionData(val positionTag: String, val currentTag: String?, val positionKey: Long)
@ -80,4 +95,5 @@ abstract class TimelineSyncManager(val context: Context) {
fun newFactory(): Factory = ServiceLoader.load(Factory::class.java).firstOrNull() ?: DummyFactory
}
}