improved send by enter feature - you can input line breaks while pressing modifier keys (ctrl, alt, shift etc)

This commit is contained in:
Mariotaku Lee 2015-10-08 21:37:41 +08:00
parent 5073d1d26e
commit 0e44a7cc51
8 changed files with 125 additions and 45 deletions

View File

@ -19,6 +19,7 @@
package org.mariotaku.twidere.activity;
import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
@ -31,11 +32,13 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.preference.PreferenceScreen;
import android.support.annotation.NonNull;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
@ -336,11 +339,25 @@ public class SettingsWizardActivity extends Activity implements Constants {
}
@Override
public void gotoNextPage() {
// Try getting location, some custom rom will popup requirement dialog
Utils.getCachedLocation(getActivity());
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
super.gotoNextPage();
}
@Override
public void gotoNextPage() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
final String[] permissions = {Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE};
requestPermissions(permissions, REQUEST_REQUEST_PERMISSIONS);
} else {
// Try getting location, some custom rom will popup requirement dialog
Utils.getCachedLocation(getActivity());
super.gotoNextPage();
}
}
}
public static class WizardPageTabsFragment extends BaseWizardPageFragment {

View File

@ -200,6 +200,7 @@ public class ComposeActivity extends ThemedFragmentActivity implements LocationL
private boolean mTextChanged;
private SetProgressVisibleRunnable mSetProgressVisibleRunnable;
private boolean mFragmentResumed;
private int mKeyMetaState;
@Override
public int getThemeColor() {
@ -311,12 +312,7 @@ public class ComposeActivity extends ThemedFragmentActivity implements LocationL
public void onClick(final View view) {
switch (view.getId()) {
case R.id.send: {
if (isQuotingProtectedStatus()) {
new RetweetProtectedStatusWarnFragment().show(getSupportFragmentManager(),
"retweet_protected_status_warning_message");
} else {
updateStatus();
}
confirmAndUpdateStatus();
break;
}
case R.id.account_selector_container: {
@ -335,6 +331,15 @@ public class ComposeActivity extends ThemedFragmentActivity implements LocationL
}
}
private void confirmAndUpdateStatus() {
if (isQuotingProtectedStatus()) {
new RetweetProtectedStatusWarnFragment().show(getSupportFragmentManager(),
"retweet_protected_status_warning_message");
} else {
updateStatus();
}
}
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
final Window window = getWindow();
@ -717,12 +722,32 @@ public class ComposeActivity extends ThemedFragmentActivity implements LocationL
mTextChanged = false;
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
final int keyCode = event.getKeyCode();
if (KeyEvent.isModifierKey(keyCode)) {
final int action = event.getAction();
if (action == MotionEvent.ACTION_DOWN) {
mKeyMetaState |= KeyboardShortcutsHandler.getMetaStateForKeyCode(keyCode);
} else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
mKeyMetaState &= ~KeyboardShortcutsHandler.getMetaStateForKeyCode(keyCode);
}
}
return super.dispatchKeyEvent(event);
}
private void setupEditText() {
final boolean sendByEnter = mPreferences.getBoolean(KEY_QUICK_SEND);
EditTextEnterHandler.attach(mEditText, new EnterListener() {
@Override
public void onHitEnter() {
updateStatus();
public boolean shouldCallListener() {
return mKeyMetaState == 0;
}
@Override
public boolean onHitEnter() {
confirmAndUpdateStatus();
return true;
}
}, sendByEnter);
mEditText.addTextChangedListener(new TextWatcher() {

View File

@ -233,8 +233,14 @@ public class QuickSearchBarActivity extends ThemedFragmentActivity implements On
EditTextEnterHandler.attach(mSearchQuery, new EnterListener() {
@Override
public void onHitEnter() {
public boolean shouldCallListener() {
return true;
}
@Override
public boolean onHitEnter() {
doSearch();
return true;
}
}, true);
mSearchQuery.addTextChangedListener(new TextWatcher() {

View File

@ -654,11 +654,17 @@ public class MessagesConversationFragment extends BaseSupportFragment implements
private void setupEditQuery() {
final EditTextEnterHandler queryEnterHandler = EditTextEnterHandler.attach(mEditUserQuery, new EnterListener() {
@Override
public void onHitEnter() {
public boolean shouldCallListener() {
return true;
}
@Override
public boolean onHitEnter() {
final ParcelableCredentials account = (ParcelableCredentials) mAccountSpinner.getSelectedItem();
if (account == null) return;
if (account == null) return false;
mEditText.setAccountId(account.account_id);
searchUsers(account.account_id, ParseUtils.parseString(mEditUserQuery.getText()), false);
return true;
}
}, true);
queryEnterHandler.addTextChangedListener(new TextWatcher() {
@ -701,8 +707,14 @@ public class MessagesConversationFragment extends BaseSupportFragment implements
private void setupEditText() {
EditTextEnterHandler.attach(mEditText, new EnterListener() {
@Override
public void onHitEnter() {
public boolean shouldCallListener() {
return true;
}
@Override
public boolean onHitEnter() {
sendDirectMessage();
return true;
}
}, mPreferences.getBoolean(KEY_QUICK_SEND, false));
mEditText.addTextChangedListener(new TextWatcher() {

View File

@ -130,12 +130,18 @@ public class RetweetQuoteDialogFragment extends BaseSupportDialogFragment implem
final boolean sendByEnter = mPreferences.getBoolean(KEY_QUICK_SEND);
final EditTextEnterHandler enterHandler = EditTextEnterHandler.attach(mEditComment, new EditTextEnterHandler.EnterListener() {
@Override
public void onHitEnter() {
public boolean shouldCallListener() {
return true;
}
@Override
public boolean onHitEnter() {
final AsyncTwitterWrapper twitter = mTwitterWrapper;
final ParcelableStatus status = getStatus();
if (twitter == null || status == null) return;
if (twitter == null || status == null) return false;
retweetOrQuote(twitter, status);
dismiss();
return true;
}
}, sendByEnter);
enterHandler.addTextChangedListener(new TextWatcher() {

View File

@ -41,6 +41,7 @@ public class EditTextEnterHandler implements View.OnKeyListener, OnEditorActionL
private EnterListener listener;
private boolean enabled;
private ArrayList<TextWatcher> textWatchers;
private boolean appendText;
public EditTextEnterHandler(@Nullable EnterListener listener, boolean enabled) {
this.listener = listener;
@ -64,27 +65,30 @@ public class EditTextEnterHandler implements View.OnKeyListener, OnEditorActionL
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
if (textWatchers == null) return;
for (TextWatcher textWatcher : textWatchers) {
textWatcher.beforeTextChanged(s, start, count, after);
if (textWatchers != null) {
for (TextWatcher textWatcher : textWatchers) {
textWatcher.beforeTextChanged(s, start, count, after);
}
}
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (textWatchers == null) return;
for (TextWatcher textWatcher : textWatchers) {
textWatcher.onTextChanged(s, start, before, count);
if (textWatchers != null) {
for (TextWatcher textWatcher : textWatchers) {
textWatcher.onTextChanged(s, start, before, count);
}
}
appendText = count > before;
}
@Override
public void afterTextChanged(final Editable s) {
final int length = s.length();
if (enabled && length > 0 && s.charAt(length - 1) == '\n') {
s.delete(length - 1, length);
if (listener != null) {
listener.onHitEnter();
if (enabled && length > 0 && s.charAt(length - 1) == '\n' && appendText) {
if (shouldCallListener()) {
s.delete(length - 1, length);
dispatchHitEnter();
}
} else if (textWatchers != null) {
for (TextWatcher textWatcher : textWatchers) {
@ -97,10 +101,7 @@ public class EditTextEnterHandler implements View.OnKeyListener, OnEditorActionL
public boolean onEditorAction(final TextView view, final int actionId, final KeyEvent event) {
if (!enabled) return false;
if (event != null && actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_DOWN) {
if (listener != null) {
listener.onHitEnter();
}
return true;
if (shouldCallListener()) return dispatchHitEnter();
}
return false;
}
@ -108,14 +109,19 @@ public class EditTextEnterHandler implements View.OnKeyListener, OnEditorActionL
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_ENTER && enabled && event.getAction() == KeyEvent.ACTION_DOWN) {
if (listener != null) {
listener.onHitEnter();
}
return true;
if (shouldCallListener()) return dispatchHitEnter();
}
return false;
}
private boolean dispatchHitEnter() {
return listener != null && listener.onHitEnter();
}
private boolean shouldCallListener() {
return listener != null && listener.shouldCallListener();
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@ -125,7 +131,9 @@ public class EditTextEnterHandler implements View.OnKeyListener, OnEditorActionL
}
public interface EnterListener {
void onHitEnter();
boolean shouldCallListener();
boolean onHitEnter();
}
}

View File

@ -768,6 +768,8 @@
<string name="dark_theme">Dark theme</string>
<string name="wizard_hint_title_location_requirement">Location permission</string>
<string name="wizard_hint_summary_location_requirement">Twidere needs location permission when you send tweet containing location.</string>
<string name="wizard_hint_title_storage_requirement">Storage permission</string>
<string name="wizard_hint_summary_storage_requirement">Twidere needs storage permission when you access documents.</string>
<string name="scrapyard">Scrapyard</string>
<string name="crop_image">Crop image</string>
<string name="usage_label_sent">Sent</string>

View File

@ -1,27 +1,31 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/hints">
<org.mariotaku.twidere.preference.ForegroundColorIconPreference
android:icon="@drawable/ic_action_location"
android:order="11"
android:summary="@string/wizard_hint_summary_location_requirement"
android:title="@string/wizard_hint_title_location_requirement"/>
android:title="@string/wizard_hint_title_location_requirement" />
<org.mariotaku.twidere.preference.ForegroundColorIconPreference
android:icon="@drawable/ic_action_storage"
android:order="12"
android:summary="@string/wizard_hint_summary_storage_requirement"
android:title="@string/wizard_hint_title_storage_requirement" />
<org.mariotaku.twidere.preference.ForegroundColorIconPreference
android:icon="@drawable/ic_action_accounts"
android:order="12"
android:order="13"
android:summary="@string/wizard_hint_compose_select_account"
android:title="@string/select_account"/>
android:title="@string/select_account" />
<org.mariotaku.twidere.preference.ForegroundColorIconPreference
android:icon="@drawable/ic_action_speaker_muted"
android:order="13"
android:order="14"
android:summary="@string/wizard_hint_filters"
android:title="@string/filters"/>
android:title="@string/filters" />
<org.mariotaku.twidere.preference.ForegroundColorIconPreference
android:icon="@drawable/ic_action_refresh"
android:order="14"
android:order="15"
android:summary="@string/wizard_hint_rate_limit"
android:title="@string/rate_limit"/>
android:title="@string/rate_limit" />
</PreferenceScreen>