code cleanup

This commit is contained in:
Mariotaku Lee 2017-01-26 23:15:05 +08:00
parent 1020ca8a5d
commit 5b19739007
No known key found for this signature in database
GPG Key ID: 15C10F89D7C33535
32 changed files with 209 additions and 284 deletions

View File

@ -11,6 +11,7 @@ import nl.komponents.kovenant.ui.failUi
import nl.komponents.kovenant.ui.successUi
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.model.sync.GoogleDriveSyncProviderInfo
import org.mariotaku.twidere.util.DebugLog
import org.mariotaku.twidere.util.TaskServiceRunner
import org.mariotaku.twidere.util.sync.*
import java.io.IOException
@ -47,9 +48,7 @@ class GoogleDriveSyncTaskRunner(context: Context, val refreshToken: String) : Sy
}.successUi {
callback(true)
}.failUi {
if (BuildConfig.DEBUG) {
Log.w(LOGTAG_SYNC, "Sync $action failed", it)
}
DebugLog.w(LOGTAG_SYNC, "Sync $action failed", it)
callback(false)
}
return true

View File

@ -5,10 +5,9 @@ import android.content.SharedPreferences;
import android.location.Location;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import android.util.Log;
import org.mariotaku.twidere.BuildConfig;
import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.util.DebugLog;
import org.mariotaku.twidere.util.JsonSerializer;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.util.dagger.DependencyHolder;
@ -28,9 +27,7 @@ public class LocationUtils implements HotMobiConstants, Constants {
final Context appContext = context.getApplicationContext();
final SharedPreferences prefs = DependencyHolder.Companion.get(context).getPreferences();
if (!prefs.getBoolean(KEY_USAGE_STATISTICS, false)) return null;
if (BuildConfig.DEBUG) {
Log.d(HotMobiLogger.LOGTAG, "getting cached location");
}
DebugLog.d(HotMobiLogger.LOGTAG, "getting cached location", null);
final Location location = Utils.getCachedLocation(appContext);
if (location == null) {
return JsonSerializer.parse(prefs.getString(KEY_FALLBACK_CACHED_LOCATION, null), LatLng.class);

View File

@ -25,16 +25,30 @@ import static org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_MEDIA
@JsonObject
public class DefaultFeatures {
private final static String REMOTE_SETTINGS_URL = "https://raw.githubusercontent.com/TwidereProject/Twidere-Android/master/twidere/src/main/assets/data/default_features.json";
private final static String REMOTE_SETTINGS_URL = "https://twidere.mariotaku.org/assets/data/default_features.json";
private static final String KEY_DEFAULT_TWITTER_CONSUMER_KEY = "default_twitter_consumer_key";
private static final String KEY_DEFAULT_TWITTER_CONSUMER_SECRET = "default_twitter_consumer_secret";
@JsonField(name = "media_link_counts_in_status")
boolean mediaLinkCountsInStatus = false;
@JsonField(name = "default_twitter_consumer_key")
String defaultTwitterConsumerKey;
@JsonField(name = "default_twitter_consumer_secret")
String defaultTwitterConsumerSecret;
public boolean isMediaLinkCountsInStatus() {
return mediaLinkCountsInStatus;
}
public String getDefaultTwitterConsumerKey() {
return defaultTwitterConsumerKey;
}
public String getDefaultTwitterConsumerSecret() {
return defaultTwitterConsumerSecret;
}
@WorkerThread
public boolean loadRemoteSettings(RestHttpClient client) throws IOException {
HttpRequest request = new HttpRequest.Builder().method(GET.METHOD).url(REMOTE_SETTINGS_URL).build();
@ -65,11 +79,15 @@ public class DefaultFeatures {
public void load(SharedPreferences preferences) {
mediaLinkCountsInStatus = preferences.getBoolean(KEY_MEDIA_LINK_COUNTS_IN_STATUS,
mediaLinkCountsInStatus);
defaultTwitterConsumerKey = preferences.getString(KEY_DEFAULT_TWITTER_CONSUMER_KEY, null);
defaultTwitterConsumerSecret = preferences.getString(KEY_DEFAULT_TWITTER_CONSUMER_SECRET, null);
}
public void save(SharedPreferences preferences) {
final SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean(KEY_MEDIA_LINK_COUNTS_IN_STATUS, mediaLinkCountsInStatus);
editor.putString(KEY_DEFAULT_TWITTER_CONSUMER_KEY, defaultTwitterConsumerKey);
editor.putString(KEY_DEFAULT_TWITTER_CONSUMER_SECRET, defaultTwitterConsumerSecret);
editor.apply();
}
}

View File

@ -31,7 +31,6 @@ import android.support.v4.util.SimpleArrayMap;
import android.support.v7.app.AlertDialog;
import android.support.v7.preference.Preference;
import android.util.AttributeSet;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -42,8 +41,8 @@ import android.widget.TextView;
import org.mariotaku.microblog.library.MicroBlog;
import org.mariotaku.microblog.library.MicroBlogException;
import org.mariotaku.microblog.library.twitter.model.Location;
import org.mariotaku.twidere.BuildConfig;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.util.DebugLog;
import org.mariotaku.twidere.util.MicroBlogAPIFactory;
import java.text.Collator;
@ -279,9 +278,7 @@ public class TrendsLocationPreference extends Preference {
}
return map.pack();
} catch (final MicroBlogException e) {
if (BuildConfig.DEBUG) {
Log.w(LOGTAG, e);
}
DebugLog.w(LOGTAG, null, e);
}
return null;
}

View File

@ -119,6 +119,7 @@ import org.mariotaku.twidere.util.ActivityTracker;
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.util.DataStoreFunctionsKt;
import org.mariotaku.twidere.util.DataStoreUtils;
import org.mariotaku.twidere.util.DebugLog;
import org.mariotaku.twidere.util.ImagePreloader;
import org.mariotaku.twidere.util.InternalTwitterContentUtils;
import org.mariotaku.twidere.util.JsonSerializer;
@ -1117,9 +1118,7 @@ public final class TwidereDataProvider extends ContentProvider implements Consta
c.addRow(new String[]{host, address.getHostAddress()});
}
} catch (final IOException ignore) {
if (BuildConfig.DEBUG) {
Log.w(LOGTAG, ignore);
}
DebugLog.w(LOGTAG, null, ignore);
}
return c;
}

View File

@ -1,73 +0,0 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 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.receiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.support.v4.net.ConnectivityManagerCompat;
import android.util.Log;
import org.mariotaku.twidere.BuildConfig;
import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.app.TwidereApplication;
import org.mariotaku.twidere.util.Utils;
import edu.tsinghua.hotmobi.HotMobiLogger;
import edu.tsinghua.hotmobi.UploadLogsService;
import edu.tsinghua.hotmobi.model.NetworkEvent;
public class ConnectivityStateReceiver extends BroadcastReceiver implements Constants {
private static final String RECEIVER_LOGTAG = LOGTAG + "." + "Connectivity";
@Override
public void onReceive(final Context context, final Intent intent) {
if (BuildConfig.DEBUG) {
Log.d(RECEIVER_LOGTAG, String.format("Received Broadcast %s", intent));
}
if (!ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) return;
final TwidereApplication application = TwidereApplication.Companion.getInstance(context);
// application.reloadConnectivitySettings();
final SharedPreferences prefs = context.getSharedPreferences(SHARED_PREFERENCES_NAME,
Context.MODE_PRIVATE);
if (prefs.getBoolean(KEY_USAGE_STATISTICS, false) && prefs.getBoolean(KEY_SETTINGS_WIZARD_COMPLETED, false)) {
// BEGIN HotMobi
final NetworkEvent event = NetworkEvent.create(context);
HotMobiLogger.getInstance(context).log(event);
// END HotMobi
}
final Context appContext = context.getApplicationContext();
final ConnectivityManager cm = (ConnectivityManager) appContext.getSystemService(Context.CONNECTIVITY_SERVICE);
final boolean isNetworkMetered = ConnectivityManagerCompat.isActiveNetworkMetered(cm);
final boolean isCharging = Utils.isCharging(appContext);
if (!isNetworkMetered && isCharging) {
final long currentTime = System.currentTimeMillis();
final long lastSuccessfulTime = HotMobiLogger.getLastUploadTime(appContext);
if ((currentTime - lastSuccessfulTime) > HotMobiLogger.UPLOAD_INTERVAL_MILLIS) {
appContext.startService(new Intent(appContext, UploadLogsService.class));
}
}
}
}

View File

@ -305,7 +305,7 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
}
public int destroyStatusAsync(final UserKey accountKey, final String statusId) {
final DestroyStatusTask task = new DestroyStatusTask(context,accountKey, statusId);
final DestroyStatusTask task = new DestroyStatusTask(context, accountKey, statusId);
return asyncTaskManager.add(task, true);
}
@ -536,9 +536,7 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
if (result.hasData()) {
handler.post(new FriendshipUpdatedEvent(accountKey, userKey, result.getData()));
} else if (result.hasException()) {
if (BuildConfig.DEBUG) {
Log.w(LOGTAG, "Unable to update friendship", result.getException());
}
DebugLog.w(LOGTAG, "Unable to update friendship", result.getException());
}
}
}.setCallback(bus));
@ -561,9 +559,7 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
try {
microBlog.setActivitiesAboutMeUnread(cursor);
} catch (MicroBlogException e) {
if (BuildConfig.DEBUG) {
Log.w(LOGTAG, e);
}
DebugLog.w(LOGTAG, null, e);
}
}
return null;
@ -685,9 +681,7 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
});
return SingleResponse.Companion.getInstance(result);
} catch (final MicroBlogException e) {
if (BuildConfig.DEBUG) {
Log.w(LOGTAG, e);
}
DebugLog.w(LOGTAG, null, e);
return SingleResponse.Companion.getInstance(e);
}
}

View File

@ -25,7 +25,6 @@ import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.v4.util.SimpleArrayMap;
import android.text.TextUtils;
import android.util.Log;
import android.webkit.MimeTypeMap;
import org.mariotaku.microblog.library.MicroBlog;
@ -45,7 +44,6 @@ import org.mariotaku.twidere.model.UserKey;
import org.mariotaku.twidere.provider.TwidereDataStore.Notifications;
import org.mariotaku.twidere.provider.TwidereDataStore.UnreadCounts;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
@ -187,11 +185,8 @@ public class TwitterWrapper implements Constants {
twitter.updateProfileBannerImage(fileBody);
} finally {
Utils.closeSilently(fileBody);
if (deleteImage && "file".equals(imageUri.getScheme())) {
final File file = new File(imageUri.getPath());
if (!file.delete()) {
Log.w(LOGTAG, String.format("Unable to delete %s", file));
}
if (deleteImage) {
Utils.deleteMedia(context, imageUri);
}
}
}
@ -208,11 +203,8 @@ public class TwitterWrapper implements Constants {
twitter.updateProfileBackgroundImage(fileBody, tile);
} finally {
Utils.closeSilently(fileBody);
if (deleteImage && "file".equals(imageUri.getScheme())) {
final File file = new File(imageUri.getPath());
if (!file.delete()) {
Log.w(LOGTAG, String.format("Unable to delete %s", file));
}
if (deleteImage) {
Utils.deleteMedia(context, imageUri);
}
}
}
@ -226,11 +218,8 @@ public class TwitterWrapper implements Constants {
return twitter.updateProfileImage(fileBody);
} finally {
Utils.closeSilently(fileBody);
if (deleteImage && "file".equals(imageUri.getScheme())) {
final File file = new File(imageUri.getPath());
if (!file.delete()) {
Log.w(LOGTAG, String.format("Unable to delete %s", file));
}
if (deleteImage) {
Utils.deleteMedia(context, imageUri);
}
}
}

View File

@ -92,6 +92,7 @@ import org.mariotaku.microblog.library.MicroBlog;
import org.mariotaku.microblog.library.MicroBlogException;
import org.mariotaku.microblog.library.twitter.model.RateLimitStatus;
import org.mariotaku.microblog.library.twitter.model.Status;
import org.mariotaku.pickncrop.library.PNCUtils;
import org.mariotaku.sqliteqb.library.AllColumns;
import org.mariotaku.sqliteqb.library.Columns;
import org.mariotaku.sqliteqb.library.Columns.Column;
@ -287,6 +288,14 @@ public final class Utils implements Constants {
return colors;
}
public static boolean deleteMedia(final Context context, final Uri uri) {
try {
return PNCUtils.deleteMedia(context, uri);
} catch (SecurityException e) {
return false;
}
}
public static class NoAccountException extends Exception {
String accountHost;
@ -1328,9 +1337,7 @@ public final class Utils implements Constants {
@Nullable
public static Location getCachedLocation(Context context) {
if (BuildConfig.DEBUG) {
Log.v(LOGTAG, "Fetching cached location", new Exception());
}
DebugLog.v(LOGTAG, "Fetching cached location", new Exception());
Location location = null;
final LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
if (lm == null) return null;

View File

@ -109,10 +109,8 @@ public class TwidereDns implements Dns, Constants {
// Return if host is an address
final List<InetAddress> fromAddressString = fromAddressString(originalHost, host);
if (fromAddressString != null) {
if (BuildConfig.DEBUG) {
addLogSplit(logger, host, "valid ip address", depth);
dumpLog(logger, fromAddressString);
}
addLogSplit(logger, host, "valid ip address", depth);
dumpLog(logger, fromAddressString);
return fromAddressString;
}
// Load from custom mapping
@ -120,9 +118,7 @@ public class TwidereDns implements Dns, Constants {
final List<InetAddress> fromMapping = getFromMapping(host);
addLogSplit(logger, host, "end custom mapping resolve", depth);
if (fromMapping != null) {
if (BuildConfig.DEBUG) {
dumpLog(logger, fromMapping);
}
dumpLog(logger, fromMapping);
return fromMapping;
}
if (useResolver) {
@ -131,9 +127,7 @@ public class TwidereDns implements Dns, Constants {
final List<InetAddress> fromSystemHosts = fromSystemHosts(host);
addLogSplit(logger, host, "end /etc/hosts resolve", depth);
if (fromSystemHosts != null) {
if (BuildConfig.DEBUG) {
dumpLog(logger, fromSystemHosts);
}
dumpLog(logger, fromSystemHosts);
return fromSystemHosts;
}
@ -142,28 +136,26 @@ public class TwidereDns implements Dns, Constants {
final List<InetAddress> fromResolver = fromResolver(originalHost, host);
addLogSplit(logger, host, "end resolver resolve", depth);
if (fromResolver != null) {
if (BuildConfig.DEBUG) {
dumpLog(logger, fromResolver);
}
dumpLog(logger, fromResolver);
return fromResolver;
}
}
addLogSplit(logger, host, "start system default resolve", depth);
final List<InetAddress> fromDefault = Arrays.asList(InetAddress.getAllByName(host));
addLogSplit(logger, host, "end system default resolve", depth);
if (BuildConfig.DEBUG) {
dumpLog(logger, fromDefault);
}
dumpLog(logger, fromDefault);
return fromDefault;
}
private void dumpLog(final TimingLogger logger, @NonNull List<InetAddress> addresses) {
if (BuildConfig.DEBUG) return;
Log.v(RESOLVER_LOGTAG, "Resolved " + addresses);
logger.dumpToLog();
}
private void addLogSplit(final TimingLogger logger, String host, String message, int depth) {
if (BuildConfig.DEBUG) return;
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < depth; i++) {
sb.append(">");

View File

@ -41,7 +41,6 @@ import org.mariotaku.microblog.library.MicroBlogException
import org.mariotaku.microblog.library.twitter.TwitterOAuth
import org.mariotaku.restfu.oauth.OAuthAuthorization
import org.mariotaku.restfu.oauth.OAuthToken
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants
import org.mariotaku.twidere.TwidereConstants.LOGTAG
@ -50,6 +49,7 @@ import org.mariotaku.twidere.extension.model.newMicroBlogInstance
import org.mariotaku.twidere.model.CustomAPIConfig
import org.mariotaku.twidere.model.SingleResponse
import org.mariotaku.twidere.util.AsyncTaskUtils
import org.mariotaku.twidere.util.DebugLog
import org.mariotaku.twidere.util.MicroBlogAPIFactory
import org.mariotaku.twidere.util.OAuthPasswordAuthenticator
import org.mariotaku.twidere.util.webkit.DefaultWebViewClient
@ -219,9 +219,7 @@ class BrowserSignInActivity : BaseActivity() {
val endpoint = MicroBlogAPIFactory.getOAuthSignInEndpoint(apiConfig.apiUrlFormat, true)
activity.loadUrl(endpoint.construct("/oauth/authorize", arrayOf("oauth_token", token.oauthToken)))
} else {
if (BuildConfig.DEBUG && result.hasException()) {
Log.w(LOGTAG, "Exception while browser sign in", result.exception)
}
DebugLog.w(LOGTAG, "Error while browser sign in", result.exception)
if (!activity.isFinishing) {
Toast.makeText(activity, R.string.message_toast_error_occurred, Toast.LENGTH_SHORT).show()
activity.finish()

View File

@ -68,8 +68,6 @@ import org.mariotaku.ktextension.checkAnySelfPermissionsGranted
import org.mariotaku.ktextension.setItemChecked
import org.mariotaku.ktextension.toTypedArray
import org.mariotaku.pickncrop.library.MediaPickerActivity
import org.mariotaku.pickncrop.library.PNCUtils
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.Constants.*
import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants
@ -1536,17 +1534,11 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
if (st == null || os == null) throw FileNotFoundException()
StreamUtils.copy(st, os, null, null)
if (deleteSrc) {
try {
PNCUtils.deleteMedia(activity, source)
} catch (e: SecurityException) {
Log.w(LOGTAG, e)
}
Utils.deleteMedia(activity, source)
}
return@mapIndexedNotNull ParcelableMediaUpdate(destination.toString(), mediaType)
} catch (e: IOException) {
if (BuildConfig.DEBUG) {
Log.w(LOGTAG, e)
}
DebugLog.w(LOGTAG, tr = e)
return@mapIndexedNotNull null
} finally {
Utils.closeSilently(os)
@ -1623,11 +1615,9 @@ class ComposeActivity : BaseActivity(), OnMenuItemClickListener, OnClickListener
}
override fun doInBackground(vararg params: Any) {
media.map { Uri.parse(it.uri) }.filter { ContentResolver.SCHEME_FILE == it.scheme }.forEach { uri ->
val file = File(uri.path)
if (!file.delete()) {
Log.d(LOGTAG, String.format("Unable to delete %s", file))
}
val activity = activityRef.get() ?: return
media.map { Uri.parse(it.uri) }.forEach { uri ->
Utils.deleteMedia(activity, uri)
}
}

View File

@ -14,7 +14,6 @@ import nl.komponents.kovenant.ui.successUi
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.R
import org.mariotaku.twidere.fragment.ProgressDialogFragment
import org.mariotaku.twidere.util.premium.ExtraFeaturesService
import java.lang.ref.WeakReference
import java.util.concurrent.atomic.AtomicBoolean
@ -46,27 +45,28 @@ class PremiumDashboardActivity : BaseActivity() {
when (item.itemId) {
R.id.consume_purchase -> {
if (BuildConfig.DEBUG) {
val dfRef = WeakReference(ProgressDialogFragment.show(supportFragmentManager, "consume_purchase_progress"))
val weakThis = WeakReference(this)
val recreate = AtomicBoolean()
task {
val activity = weakThis.get() ?: throw IllegalStateException()
if (!activity.extraFeaturesService.destroyPurchase()) {
throw IllegalStateException()
}
}.successUi {
recreate.set(true)
}.failUi {
val activity = weakThis.get() ?: return@failUi
Toast.makeText(activity, R.string.message_unable_to_consume_purchase, Toast.LENGTH_SHORT).show()
}.alwaysUi {
weakThis.get()?.executeAfterFragmentResumed {
val fm = weakThis.get()?.supportFragmentManager
val df = dfRef.get() ?: (fm?.findFragmentByTag("consume_purchase_progress") as? DialogFragment)
df?.dismiss()
if (recreate.get()) {
weakThis.get()?.recreate()
}
return true
}
val dfRef = WeakReference(ProgressDialogFragment.show(supportFragmentManager, "consume_purchase_progress"))
val weakThis = WeakReference(this)
val recreate = AtomicBoolean()
task {
val activity = weakThis.get() ?: throw IllegalStateException()
if (!activity.extraFeaturesService.destroyPurchase()) {
throw IllegalStateException()
}
}.successUi {
recreate.set(true)
}.failUi {
val activity = weakThis.get() ?: return@failUi
Toast.makeText(activity, R.string.message_unable_to_consume_purchase, Toast.LENGTH_SHORT).show()
}.alwaysUi {
weakThis.get()?.executeAfterFragmentResumed {
val fm = weakThis.get()?.supportFragmentManager
val df = dfRef.get() ?: (fm?.findFragmentByTag("consume_purchase_progress") as? DialogFragment)
df?.dismiss()
if (recreate.get()) {
weakThis.get()?.recreate()
}
}
}

View File

@ -42,7 +42,6 @@ import android.text.Editable
import android.text.InputType
import android.text.TextUtils
import android.text.TextWatcher
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import android.view.View
@ -367,9 +366,7 @@ class SignInActivity : BaseActivity(), OnClickListener, TextWatcher, APIEditorDi
}
internal fun onSignInError(exception: Exception) {
if (BuildConfig.DEBUG) {
Log.w(LOGTAG, exception)
}
DebugLog.w(LOGTAG, "Sign in error", exception)
var errorReason: String? = null
if (exception is AuthenticityTokenException) {
Toast.makeText(this, R.string.message_toast_wrong_api_key, Toast.LENGTH_SHORT).show()

View File

@ -27,7 +27,6 @@ import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import android.os.AsyncTask
import android.support.multidex.MultiDex
import android.util.Log
import nl.komponents.kovenant.android.startKovenant
import nl.komponents.kovenant.android.stopKovenant
import nl.komponents.kovenant.task
@ -130,13 +129,9 @@ class TwidereApplication : Application(), Constants, OnSharedPreferenceChangeLis
defaultFeatures.loadRemoteSettings(restHttpClient)
}.success {
defaultFeatures.save(sharedPreferences)
if (BuildConfig.DEBUG) {
Log.d(LOGTAG, "Loaded remote features")
}
DebugLog.d(LOGTAG, "Loaded remote features")
}.fail {
if (BuildConfig.DEBUG) {
Log.w(LOGTAG, "Unable to load remote features", it)
}
DebugLog.w(LOGTAG, "Unable to load remote features", it)
}.always {
kPreferences[defaultFeatureLastUpdated] = System.currentTimeMillis()
}

View File

@ -6,9 +6,7 @@ import android.text.TextUtils
import org.mariotaku.kpreferences.*
import org.mariotaku.ktextension.toLong
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.Constants.KEY_DISPLAY_PROFILE_IMAGE
import org.mariotaku.twidere.Constants.KEY_NO_CLOSE_AFTER_TWEET_SENT
import org.mariotaku.twidere.TwidereConstants.*
import org.mariotaku.twidere.Constants.*
import org.mariotaku.twidere.annotation.AccountType
import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_COMPOSE_ACCOUNTS
import org.mariotaku.twidere.constant.SharedPreferenceConstants.KEY_DISPLAY_SENSITIVE_CONTENTS
@ -59,6 +57,7 @@ val themeColorKey = KIntKey(KEY_THEME_COLOR, 0)
val filterUnavailableQuoteStatusesKey = KBooleanKey("filter_unavailable_quote_statuses", false)
val filterPossibilitySensitiveStatusesKey = KBooleanKey("filter_possibility_sensitive_statuses", false)
val chromeCustomTabKey = KBooleanKey("chrome_custom_tab", true)
val usageStatisticsKey = KBooleanKey(KEY_USAGE_STATISTICS, false)
object themeBackgroundAlphaKey : KSimpleKey<Int>(KEY_THEME_BACKGROUND_ALPHA, 0xFF) {
override fun read(preferences: SharedPreferences): Int {

View File

@ -15,7 +15,6 @@ import com.bluelinelabs.logansquare.LoganSquare
import org.mariotaku.restfu.annotation.method.GET
import org.mariotaku.restfu.http.HttpRequest
import org.mariotaku.restfu.http.RestHttpClient
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.R
import org.mariotaku.twidere.adapter.ArrayAdapter
import org.mariotaku.twidere.adapter.BaseArrayAdapter
@ -148,9 +147,7 @@ class APIEditorDialogFragment : BaseDialogFragment() {
adapter = CustomAPIConfigArrayAdapter(context, configs)
val builder = AlertDialog.Builder(context)
builder.setAdapter(adapter, this)
if (!BuildConfig.DEBUG) {
loaderManager.initLoader(0, null, this)
}
loaderManager.initLoader(0, null, this)
return builder.create()
}

View File

@ -30,7 +30,6 @@ import android.support.v4.content.Loader
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.support.v7.widget.RecyclerView.OnScrollListener
import android.util.Log
import android.view.*
import com.squareup.otto.Subscribe
import edu.tsinghua.hotmobi.HotMobiLogger
@ -39,8 +38,6 @@ import kotlinx.android.synthetic.main.fragment_content_recyclerview.*
import org.mariotaku.kpreferences.get
import org.mariotaku.ktextension.isNullOrEmpty
import org.mariotaku.ktextension.rangeOfSize
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.Constants.KEY_NEW_DOCUMENT_API
import org.mariotaku.twidere.R
import org.mariotaku.twidere.TwidereConstants
import org.mariotaku.twidere.adapter.ParcelableActivitiesAdapter
@ -286,9 +283,7 @@ abstract class AbsActivitiesFragment protected constructor() :
override fun onGapClick(holder: GapViewHolder, position: Int) {
val activity = adapter.getActivity(position) ?: return
if (BuildConfig.DEBUG) {
Log.v(TwidereConstants.LOGTAG, "Load activity gap $activity")
}
DebugLog.v(TwidereConstants.LOGTAG, "Load activity gap $activity")
val accountIds = arrayOf(activity.account_key)
val maxIds = arrayOf(activity.min_position)
val maxSortIds = longArrayOf(activity.min_sort_position)

View File

@ -344,9 +344,7 @@ abstract class AbsStatusesFragment protected constructor() :
override fun onGapClick(holder: GapViewHolder, position: Int) {
val adapter = this.adapter
val status = adapter.getStatus(position) ?: return
if (BuildConfig.DEBUG) {
Log.v(TwidereConstants.LOGTAG, "Load activity gap " + status)
}
DebugLog.v(TwidereConstants.LOGTAG, "Load activity gap " + status)
adapter.addGapLoadingId(ObjectId(status.account_key, status.id))
val accountIds = arrayOf(status.account_key)
val maxIds = arrayOf<String?>(status.id)

View File

@ -238,8 +238,8 @@ abstract class MicroBlogAPIStatusesLoader(
}) { current, total -> true }
} catch (e: Exception) {
// Ignore
if (BuildConfig.DEBUG && e !is IOException) {
Log.w(LOGTAG, e)
if (e !is IOException) {
DebugLog.w(LOGTAG, "Error saving cached data", e)
}
}

View File

@ -0,0 +1,72 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2014 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.receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.net.ConnectivityManager
import android.support.v4.net.ConnectivityManagerCompat
import edu.tsinghua.hotmobi.HotMobiLogger
import edu.tsinghua.hotmobi.UploadLogsService
import edu.tsinghua.hotmobi.model.NetworkEvent
import org.mariotaku.kpreferences.get
import org.mariotaku.twidere.TwidereConstants
import org.mariotaku.twidere.TwidereConstants.SHARED_PREFERENCES_NAME
import org.mariotaku.twidere.app.TwidereApplication
import org.mariotaku.twidere.constant.usageStatisticsKey
import org.mariotaku.twidere.util.DebugLog
import org.mariotaku.twidere.util.Utils
class ConnectivityStateReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
DebugLog.d(RECEIVER_LOGTAG, String.format("Received Broadcast %s", intent), null)
if (ConnectivityManager.CONNECTIVITY_ACTION != intent.action) return
val application = TwidereApplication.getInstance(context)
// application.reloadConnectivitySettings();
val prefs = context.getSharedPreferences(SHARED_PREFERENCES_NAME,
Context.MODE_PRIVATE)
if (prefs[usageStatisticsKey]) {
// BEGIN HotMobi
val event = NetworkEvent.create(context)
HotMobiLogger.getInstance(context).log(event)
// END HotMobi
}
val appContext = context.applicationContext
val cm = appContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val isNetworkMetered = ConnectivityManagerCompat.isActiveNetworkMetered(cm)
val isCharging = Utils.isCharging(appContext)
if (!isNetworkMetered && isCharging) {
val currentTime = System.currentTimeMillis()
val lastSuccessfulTime = HotMobiLogger.getLastUploadTime(appContext)
if (currentTime - lastSuccessfulTime > HotMobiLogger.UPLOAD_INTERVAL_MILLIS) {
appContext.startService(Intent(appContext, UploadLogsService::class.java))
}
}
}
companion object {
private val RECEIVER_LOGTAG = TwidereConstants.LOGTAG + "build/intermediates/exploded-aar/com.lnikkila/extendedtouchview/0.1.0/res" + "Connectivity"
}
}

View File

@ -34,6 +34,7 @@ import org.mariotaku.twidere.model.util.AccountUtils
import org.mariotaku.twidere.provider.TwidereDataStore.*
import org.mariotaku.twidere.util.ContentValuesCreator
import org.mariotaku.twidere.util.DataStoreUtils
import org.mariotaku.twidere.util.DebugLog
import org.mariotaku.twidere.util.TwidereArrayUtils
import java.io.ByteArrayOutputStream
import java.io.IOException
@ -56,9 +57,7 @@ class StreamingService : Service() {
override fun onCreate() {
super.onCreate()
notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (BuildConfig.DEBUG) {
Log.d(LOGTAG, "Stream service started.")
}
DebugLog.d(LOGTAG, "Stream service started.")
initStreaming()
AccountManager.get(this).addOnAccountsUpdatedListenerSafe(accountChangeObserver, updateImmediately = false)
}
@ -66,9 +65,7 @@ class StreamingService : Service() {
override fun onDestroy() {
clearTwitterInstances()
AccountManager.get(this).removeOnAccountsUpdatedListenerSafe(accountChangeObserver)
if (BuildConfig.DEBUG) {
Log.d(LOGTAG, "Stream service stopped.")
}
DebugLog.d(LOGTAG, "Stream service stopped.")
super.onDestroy()
}
@ -97,9 +94,7 @@ class StreamingService : Service() {
val accountsList = AccountUtils.getAllAccountDetails(AccountManager.get(this), true).filter { it.credentials is OAuthCredentials }
val accountKeys = accountsList.map { it.key }.toTypedArray()
val activatedPreferences = AccountPreferences.getAccountPreferences(this, accountKeys)
if (BuildConfig.DEBUG) {
Log.d(LOGTAG, "Setting up twitter stream instances")
}
DebugLog.d(LOGTAG, "Setting up twitter stream instances")
this.accountKeys = accountKeys
clearTwitterInstances()
var result = false

View File

@ -2,7 +2,6 @@ package org.mariotaku.twidere.task
import android.content.Context
import android.net.Uri
import android.util.Log
import com.squareup.otto.Bus
import org.apache.commons.lang3.math.NumberUtils
import org.mariotaku.abstask.library.AbstractTask
@ -13,7 +12,6 @@ import org.mariotaku.microblog.library.twitter.model.DirectMessage
import org.mariotaku.microblog.library.twitter.model.ErrorInfo
import org.mariotaku.microblog.library.twitter.model.Paging
import org.mariotaku.microblog.library.twitter.model.ResponseList
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.TwidereConstants
import org.mariotaku.twidere.constant.loadItemLimitKey
import org.mariotaku.twidere.model.RefreshTaskParam
@ -90,9 +88,7 @@ abstract class GetDirectMessagesTask(
} else if (e.isCausedByNetworkIssue) {
errorInfoStore[ErrorInfoStore.KEY_DIRECT_MESSAGES, accountKey] = ErrorInfoStore.CODE_NETWORK_ERROR
}
if (BuildConfig.DEBUG) {
Log.w(TwidereConstants.LOGTAG, e)
}
DebugLog.w(TwidereConstants.LOGTAG, tr = e)
result.add(TwitterWrapper.MessageListResponse(accountKey, e))
}

View File

@ -11,6 +11,7 @@ import org.mariotaku.twidere.model.SingleResponse
import org.mariotaku.twidere.model.UserKey
import org.mariotaku.twidere.provider.TwidereDataStore.SavedSearches
import org.mariotaku.twidere.util.ContentValuesCreator
import org.mariotaku.twidere.util.DebugLog
import org.mariotaku.twidere.util.MicroBlogAPIFactory
import org.mariotaku.twidere.util.content.ContentResolverUtils
@ -34,9 +35,7 @@ class GetSavedSearchesTask(
cr.delete(SavedSearches.CONTENT_URI, where.sql, whereArgs)
ContentResolverUtils.bulkInsert(cr, SavedSearches.CONTENT_URI, values)
} catch (e: MicroBlogException) {
if (BuildConfig.DEBUG) {
Log.w(LOGTAG, e)
}
DebugLog.w(LOGTAG, tr = e)
}
}
return SingleResponse.getInstance(Unit)

View File

@ -6,7 +6,6 @@ import android.content.ContentValues
import android.content.Context
import android.net.Uri
import android.support.annotation.UiThread
import android.util.Log
import com.squareup.otto.Bus
import org.mariotaku.abstask.library.AbstractTask
import org.mariotaku.kpreferences.KPreferences
@ -16,7 +15,6 @@ import org.mariotaku.microblog.library.twitter.model.Activity
import org.mariotaku.microblog.library.twitter.model.Paging
import org.mariotaku.microblog.library.twitter.model.ResponseList
import org.mariotaku.sqliteqb.library.Expression
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.TwidereConstants.LOGTAG
import org.mariotaku.twidere.TwidereConstants.QUERY_PARAM_NOTIFY
import org.mariotaku.twidere.constant.loadItemLimitKey
@ -103,9 +101,7 @@ abstract class GetActivitiesTask(
}
errorInfoStore.remove(errorInfoKey, accountKey)
} catch (e: MicroBlogException) {
if (BuildConfig.DEBUG) {
Log.w(LOGTAG, e)
}
DebugLog.w(LOGTAG, tr = e)
if (e.errorCode == 220) {
errorInfoStore[errorInfoKey, accountKey] = ErrorInfoStore.CODE_NO_ACCESS_FOR_CREDENTIALS
} else if (e.isCausedByNetworkIssue) {

View File

@ -4,7 +4,6 @@ import android.accounts.AccountManager
import android.content.ContentValues
import android.content.Context
import android.net.Uri
import android.util.Log
import com.squareup.otto.Bus
import edu.tsinghua.hotmobi.HotMobiLogger
import edu.tsinghua.hotmobi.model.RefreshEvent
@ -20,7 +19,6 @@ import org.mariotaku.microblog.library.twitter.model.ResponseList
import org.mariotaku.microblog.library.twitter.model.Status
import org.mariotaku.sqliteqb.library.Columns
import org.mariotaku.sqliteqb.library.Expression
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.TwidereConstants.LOGTAG
import org.mariotaku.twidere.TwidereConstants.QUERY_PARAM_NOTIFY
import org.mariotaku.twidere.constant.loadItemLimitKey
@ -139,9 +137,7 @@ abstract class GetStatusesTask(
TaskStarter.execute(cacheTask)
errorInfoStore.remove(errorInfoKey, accountKey.id)
} catch (e: MicroBlogException) {
if (BuildConfig.DEBUG) {
Log.w(LOGTAG, e)
}
DebugLog.w(LOGTAG, tr = e)
if (e.isCausedByNetworkIssue) {
errorInfoStore[errorInfoKey, accountKey.id] = ErrorInfoStore.CODE_NETWORK_ERROR
} else if (e.statusCode == 401) {

View File

@ -732,12 +732,7 @@ class UpdateStatusTask(
data class UriMediaDeletionItem(val uri: Uri) : MediaDeletionItem {
override fun delete(context: Context): Boolean {
try {
return PNCUtils.deleteMedia(context, uri)
} catch (e: SecurityException) {
// Ignore
return false
}
return Utils.deleteMedia(context, uri)
}
}

View File

@ -6,7 +6,6 @@ import android.content.SharedPreferences
import android.net.Uri
import org.mariotaku.kpreferences.get
import org.mariotaku.ktextension.useCursor
import org.mariotaku.pickncrop.library.PNCUtils
import org.mariotaku.sqliteqb.library.*
import org.mariotaku.twidere.constant.filterPossibilitySensitiveStatusesKey
import org.mariotaku.twidere.constant.filterUnavailableQuoteStatusesKey
@ -94,11 +93,7 @@ fun deleteDrafts(context: Context, draftIds: LongArray): Int {
while (!cursor.isAfterLast) {
val draft = indices.newObject(cursor)
draft.media?.forEach { item ->
try {
PNCUtils.deleteMedia(context, Uri.parse(item.uri))
} catch (e: SecurityException) {
// Ignore
}
Utils.deleteMedia(context, Uri.parse(item.uri))
}
cursor.moveToNext()
}

View File

@ -8,6 +8,17 @@ import org.mariotaku.twidere.BuildConfig
*/
object DebugLog {
@JvmStatic
fun v(tag: String, msg: String, tr: Throwable? = null): Int {
if (!BuildConfig.DEBUG) return 0
if (tr != null) {
return Log.v(tag, msg, tr)
} else {
return Log.v(tag, msg)
}
}
@JvmStatic
fun d(tag: String, msg: String, tr: Throwable? = null): Int {
if (!BuildConfig.DEBUG) return 0
if (tr != null) {
@ -17,6 +28,7 @@ object DebugLog {
}
}
@JvmStatic
fun w(tag: String, msg: String? = null, tr: Throwable? = null): Int {
if (!BuildConfig.DEBUG) return 0
if (msg != null && tr != null) {

View File

@ -2,17 +2,16 @@ package org.mariotaku.twidere.util.sync
import android.content.Context
import android.support.v4.util.LongSparseArray
import android.util.Log
import org.mariotaku.ktextension.map
import org.mariotaku.ktextension.set
import org.mariotaku.sqliteqb.library.Expression
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.extension.model.filename
import org.mariotaku.twidere.extension.model.unique_id_non_null
import org.mariotaku.twidere.model.Draft
import org.mariotaku.twidere.model.DraftCursorIndices
import org.mariotaku.twidere.model.DraftValuesCreator
import org.mariotaku.twidere.provider.TwidereDataStore.Drafts
import org.mariotaku.twidere.util.DebugLog
import org.mariotaku.twidere.util.content.ContentResolverUtils
import java.io.File
import java.io.FileNotFoundException
@ -27,9 +26,7 @@ abstract class FileBasedDraftsSyncAction<RemoteFileInfo>(val context: Context) :
@Throws(IOException::class)
override fun execute(): Boolean {
if (BuildConfig.DEBUG) {
Log.d(LOGTAG_SYNC, "Begin syncing drafts")
}
DebugLog.d(LOGTAG_SYNC, "Begin syncing drafts")
if (!setup()) {
return false
@ -114,29 +111,23 @@ abstract class FileBasedDraftsSyncAction<RemoteFileInfo>(val context: Context) :
// Upload local items
if (uploadLocalList.isNotEmpty()) {
if (BuildConfig.DEBUG) {
val fileList = uploadLocalList.joinToString(",") { it.filename }
Log.d(LOGTAG_SYNC, "Uploading local drafts $fileList")
}
val fileList = uploadLocalList.joinToString(",") { it.filename }
DebugLog.d(LOGTAG_SYNC, "Uploading local drafts $fileList")
uploadDrafts(uploadLocalList)
}
// Download remote items
if (downloadRemoteInfoList.isNotEmpty()) {
if (BuildConfig.DEBUG) {
val fileList = downloadRemoteInfoList.joinToString(",") { it.draftFileName }
Log.d(LOGTAG_SYNC, "Downloading remote drafts $fileList")
}
val fileList = downloadRemoteInfoList.joinToString(",") { it.draftFileName }
DebugLog.d(LOGTAG_SYNC, "Downloading remote drafts $fileList")
ContentResolverUtils.bulkInsert(context.contentResolver, Drafts.CONTENT_URI,
downloadDrafts(downloadRemoteInfoList).map { DraftValuesCreator.create(it) })
}
// Update local items
if (updateLocalInfoList.size() > 0) {
if (BuildConfig.DEBUG) {
val fileList = (0 until updateLocalInfoList.size()).joinToString(",") { updateLocalInfoList.valueAt(it).draftFileName }
Log.d(LOGTAG_SYNC, "Updating local drafts $fileList")
}
val fileList = (0 until updateLocalInfoList.size()).joinToString(",") { updateLocalInfoList.valueAt(it).draftFileName }
DebugLog.d(LOGTAG_SYNC, "Updating local drafts $fileList")
for (index in 0 until updateLocalInfoList.size()) {
val draft = Draft()
if (draft.loadFromRemote(updateLocalInfoList.valueAt(index))) {
@ -149,20 +140,16 @@ abstract class FileBasedDraftsSyncAction<RemoteFileInfo>(val context: Context) :
// Remove local items
if (removeLocalIdsList.isNotEmpty()) {
if (BuildConfig.DEBUG) {
val fileList = removeLocalIdsList.joinToString(",") { "$it.eml" }
Log.d(LOGTAG_SYNC, "Removing local drafts $fileList")
}
val fileList = removeLocalIdsList.joinToString(",") { "$it.eml" }
DebugLog.d(LOGTAG_SYNC, "Removing local drafts $fileList")
ContentResolverUtils.bulkDelete(context.contentResolver, Drafts.CONTENT_URI,
Drafts.UNIQUE_ID, removeLocalIdsList, null)
}
// Remove remote items
if (removeRemoteInfoList.isNotEmpty()) {
if (BuildConfig.DEBUG) {
val fileList = removeRemoteInfoList.joinToString(",") { it.draftFileName }
Log.d(LOGTAG_SYNC, "Removing remote drafts $fileList")
}
val fileList = removeRemoteInfoList.joinToString(",") { it.draftFileName }
DebugLog.d(LOGTAG_SYNC, "Removing remote drafts $fileList")
removeDrafts(removeRemoteInfoList)
}
@ -178,9 +165,7 @@ abstract class FileBasedDraftsSyncAction<RemoteFileInfo>(val context: Context) :
}
}
if (BuildConfig.DEBUG) {
Log.d(LOGTAG_SYNC, "Finished syncing drafts")
}
DebugLog.d(LOGTAG_SYNC, "Finished syncing drafts")
return true
}

View File

@ -2,6 +2,7 @@ package org.mariotaku.twidere.util.sync
import android.util.Log
import org.mariotaku.twidere.BuildConfig
import org.mariotaku.twidere.util.DebugLog
import java.io.Closeable
import java.io.FileNotFoundException
import java.io.IOException
@ -13,9 +14,7 @@ abstract class SingleFileBasedDataSyncAction<Data, SnapshotStore, DownloadSessio
private val ATTR_KEY = "key"
override final fun execute(): Boolean {
if (BuildConfig.DEBUG) {
Log.d(LOGTAG_SYNC, "Begin syncing $whatData")
}
DebugLog.d(LOGTAG_SYNC, "Begin syncing $whatData")
if (!setup()) {
return false
@ -36,8 +35,8 @@ abstract class SingleFileBasedDataSyncAction<Data, SnapshotStore, DownloadSessio
Log.d(LOGTAG_SYNC, "Downloading remote $whatData")
}
remoteData = it.loadFromRemote()
} else if (BuildConfig.DEBUG) {
Log.d(LOGTAG_SYNC, "Remote $whatData unchanged, skip download")
} else {
DebugLog.d(LOGTAG_SYNC, "Remote $whatData unchanged, skip download")
}
}
} catch (e: FileNotFoundException) {
@ -85,9 +84,7 @@ abstract class SingleFileBasedDataSyncAction<Data, SnapshotStore, DownloadSessio
val localModifiedTime = System.currentTimeMillis()
if (shouldCreateRemote || localModified) {
if (BuildConfig.DEBUG) {
Log.d(LOGTAG_SYNC, "Uploading $whatData")
}
DebugLog.d(LOGTAG_SYNC, "Uploading $whatData")
newSaveToRemoteSession().use {
it.setRemoteLastModified(localModifiedTime)
it.saveToRemote(localData)
@ -102,9 +99,7 @@ abstract class SingleFileBasedDataSyncAction<Data, SnapshotStore, DownloadSessio
// Ignore
}
if (BuildConfig.DEBUG) {
Log.d(LOGTAG_SYNC, "Finished syncing $whatData")
}
DebugLog.d(LOGTAG_SYNC, "Finished syncing $whatData")
return true
}

View File

@ -16,6 +16,7 @@
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<!-- Don't remove this FrameLayout!!! Or content will get fucked up when switching pages! -->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"