Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ android {
viewBinding = true
buildConfig = true
compose = true
aidl = true
}

externalNativeBuild {
Expand Down
26 changes: 26 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,17 @@ SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
android:theme="@style/TransparentActivityTheme" android:excludeFromRecents="true" android:noHistory="true"
android:windowSoftInputMode="adjustResize|stateAlwaysVisible" android:configChanges="orientation|screenSize"/>

<activity android:name="helium314.keyboard.latin.media.MediaReceiverActivity"
android:theme="@style/TransparentActivityTheme"
android:exported="true"
android:excludeFromRecents="true"
android:noHistory="true">
<intent-filter>
<action android:name="helium314.keyboard.action.SEND_MEDIA_TO_IME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>

<!-- Broadcast receivers -->
<receiver android:name="SystemBroadcastReceiver"
android:exported="true">
Expand Down Expand Up @@ -117,6 +128,16 @@ SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/gesture_data_path"/>
</provider>

<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.mediafileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/media_file_provider_paths" />
</provider>
</application>

<queries>
Expand All @@ -132,5 +153,10 @@ SPDX-License-Identifier: Apache-2.0 AND GPL-3.0-only
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent>

<!-- To detect media provider plugins -->
<intent>
<action android:name="com.heliboard.intent.MEDIA_PROVIDER" />
</intent>
</queries>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package helium314.keyboard.latin.media;

interface IMediaProviderService {
Bundle discoverCapabilities();
Bundle search(String query, in Bundle options);
Bundle browse(String parentId, in Bundle options);
Bundle getContent(String itemId, in Bundle options);
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ public interface KeyboardActionListener {
*/
void onTextInput(String text);

void onMediaPickerRequested();

/**
* Called when user started batch input.
*/
Expand Down Expand Up @@ -136,6 +138,8 @@ public void onCodeInput(int primaryCode, int x, int y, boolean isKeyRepeat) {}
@Override
public void onTextInput(String text) {}
@Override
public void onMediaPickerRequested() {}
@Override
public void onStartBatchInput() {}
@Override
public void onUpdateBatchInput(InputPointers batchPointers) {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ class KeyboardActionListenerImpl(private val latinIME: LatinIME, private val inp
}

override fun onCodeInput(primaryCode: Int, x: Int, y: Int, isKeyRepeat: Boolean) {
if (latinIME.consumeMediaPickerCodeInput(primaryCode)) return
when (primaryCode) {
KeyCode.TOGGLE_AUTOCORRECT -> return settings.toggleAutoCorrect()
KeyCode.TOGGLE_INCOGNITO_MODE -> return settings.toggleAlwaysIncognitoMode()
Expand All @@ -119,7 +120,12 @@ class KeyboardActionListenerImpl(private val latinIME: LatinIME, private val inp
metaAfterCodeInput(primaryCode)
}

override fun onTextInput(text: String?) = latinIME.onTextInput(text)
override fun onTextInput(text: String?) {
if (latinIME.consumeMediaPickerTextInput(text)) return
latinIME.onTextInput(text)
}

override fun onMediaPickerRequested() = latinIME.launchMediaPicker()

override fun onStartBatchInput() = latinIME.onStartBatchInput()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
*/
public final class EmojiPalettesView extends LinearLayout
implements View.OnClickListener, EmojiViewCallback {
private static final String MEDIA_TAB_TAG = "media";
private static final class PagerViewHolder extends RecyclerView.ViewHolder {
private long mCategoryId;

Expand Down Expand Up @@ -245,6 +246,19 @@ private void addTab(final LinearLayout host, final int categoryId) {
iconView.setOnClickListener(this);
}

private void addMediaTab(final LinearLayout host) {
final ImageView iconView = new ImageView(getContext());
mColors.setBackground(iconView, ColorType.STRIP_BACKGROUND);
mColors.setColor(iconView, ColorType.EMOJI_CATEGORY);
iconView.setScaleType(ImageView.ScaleType.CENTER);
iconView.setImageResource(R.drawable.ic_image);
iconView.setContentDescription("Media");
iconView.setTag(MEDIA_TAB_TAG);
host.addView(iconView);
iconView.setLayoutParams(new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT, 1f));
iconView.setOnClickListener(this);
}

@SuppressLint("ClickableViewAccessibility")
public void initialize() { // needs to be delayed for access to EmojiTabStrip, which is not a child of this view
if (initialized) return;
Expand All @@ -254,6 +268,9 @@ public void initialize() { // needs to be delayed for access to EmojiTabStrip, w
for (final EmojiCategory.CategoryProperties properties : mEmojiCategory.getShownCategories()) {
addTab(mTabStrip, properties.mCategoryId);
}
if (Settings.getValues().mMediaPluginsEnabled) {
addMediaTab(mTabStrip);
}
}

mPager = findViewById(R.id.emoji_pager);
Expand Down Expand Up @@ -281,6 +298,9 @@ public void onClick(View v) {
setCurrentCategoryId(categoryId, false);
updateEmojiCategoryPageIdView();
}
} else if (MEDIA_TAB_TAG.equals(tag)) {
AudioAndHapticFeedbackManager.getInstance().performHapticAndAudioFeedback(KeyCode.NOT_SPECIFIED, this, HapticEvent.KEY_PRESS);
mKeyboardActionListener.onMediaPickerRequested();
}
}

Expand Down Expand Up @@ -336,6 +356,7 @@ public void setHardwareAcceleratedDrawingEnabled(final boolean enabled) {
public void startEmojiPalettes(final KeyVisualAttributes keyVisualAttr,
final EditorInfo editorInfo, final KeyboardActionListener keyboardActionListener) {
initialize();
mKeyboardActionListener = keyboardActionListener;

setupBottomRowKeyboard(editorInfo, keyboardActionListener);
final KeyDrawParams params = new KeyDrawParams();
Expand Down
73 changes: 72 additions & 1 deletion app/src/main/java/helium314/keyboard/latin/LatinIME.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import android.graphics.Color;
import android.inputmethodservice.InputMethodService;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Debug;
Expand All @@ -32,7 +33,9 @@
import android.view.inputmethod.InlineSuggestion;
import android.view.inputmethod.InlineSuggestionsRequest;
import android.view.inputmethod.InlineSuggestionsResponse;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodSubtype;
import android.widget.Toast;

import helium314.keyboard.accessibility.AccessibilityUtils;
import helium314.keyboard.compat.ConfigurationCompatKt;
Expand Down Expand Up @@ -62,6 +65,11 @@
import helium314.keyboard.latin.common.ViewOutlineProviderUtilsKt;
import helium314.keyboard.latin.define.DebugFlags;
import helium314.keyboard.latin.inputlogic.InputLogic;
import helium314.keyboard.latin.media.MediaInsertionController;
import helium314.keyboard.latin.media.MediaInsertionDispatcher;
import helium314.keyboard.latin.media.MediaPluginContract;
import helium314.keyboard.latin.media.provider.MediaPickerPopup;
import helium314.keyboard.latin.media.provider.MediaProviderItem;
import helium314.keyboard.latin.personalization.PersonalizationHelper;
import helium314.keyboard.latin.settings.Settings;
import helium314.keyboard.latin.settings.SettingsValues;
Expand Down Expand Up @@ -114,7 +122,6 @@ public class LatinIME extends InputMethodService implements
private static final int PENDING_IMS_CALLBACK_DURATION_MILLIS = 800;
static final long DELAY_WAIT_FOR_DICTIONARY_LOAD_MILLIS = TimeUnit.SECONDS.toMillis(2);
static final long DELAY_DEALLOCATE_MEMORY_MILLIS = TimeUnit.SECONDS.toMillis(10);

/**
* The name of the scheme used by the Package Manager to warn of a new package installation,
* replacement or removal.
Expand Down Expand Up @@ -149,6 +156,10 @@ public class LatinIME extends InputMethodService implements
// Used for re-initialize keyboard layout after onConfigurationChange.
@Nullable
private Context mDisplayContext;
private final MediaInsertionController mMediaInsertionController =
new MediaInsertionController(this);
@Nullable
private MediaPickerPopup mActiveMediaPickerPopup;

// Object for reacting to adding/removing a dictionary pack.
private final BroadcastReceiver mDictionaryPackInstallReceiver =
Expand Down Expand Up @@ -577,6 +588,7 @@ public void onCreate() {
restartAfterUnlockFilter.addAction(Intent.ACTION_USER_UNLOCKED);
registerReceiver(mRestartAfterDeviceUnlockReceiver, restartAfterUnlockFilter);

MediaInsertionDispatcher.register(this);
StatsUtils.onCreate(mSettings.getCurrent(), mRichImm);
}

Expand Down Expand Up @@ -698,6 +710,7 @@ public void onDestroy() {
unregisterReceiver(mDictionaryPackInstallReceiver);
unregisterReceiver(mDictionaryDumpBroadcastReceiver);
unregisterReceiver(mRestartAfterDeviceUnlockReceiver);
MediaInsertionDispatcher.unregister(this);
mStatsUtilsManager.onDestroy(this /* context */);
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
Expand Down Expand Up @@ -1723,6 +1736,64 @@ public void launchEmojiSearch() {
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_MULTIPLE_TASK));
}

public void launchMediaPicker() {
if (!mSettings.getCurrent().mMediaPluginsEnabled) {
Log.d(MediaPluginContract.LOG_TAG, "Media plugins disabled");
return;
}
if (mActiveMediaPickerPopup != null) {
mActiveMediaPickerPopup.dismiss();
return;
}
if (mInputView == null) {
Toast.makeText(this, "Open a text field first", Toast.LENGTH_SHORT).show();
return;
}
new MediaPickerPopup(this, mInputView, getPreferredMediaMaxBytes()).show();
}

public void setActiveMediaPickerPopup(final MediaPickerPopup popup) {
mActiveMediaPickerPopup = popup;
}

public void clearActiveMediaPickerPopup(final MediaPickerPopup popup) {
if (mActiveMediaPickerPopup == popup) {
mActiveMediaPickerPopup = null;
}
}

public boolean consumeMediaPickerCodeInput(final int primaryCode) {
return mActiveMediaPickerPopup != null
&& mActiveMediaPickerPopup.handleCodeInput(primaryCode);
}

public boolean consumeMediaPickerTextInput(@Nullable final String text) {
return mActiveMediaPickerPopup != null
&& mActiveMediaPickerPopup.handleTextInput(text);
}

public void onExternalMediaRequested(final Uri uri, final String mime, final String label) {
insertExternalMedia(uri, mime, label);
}

public void insertExternalMedia(final Uri uri, final String mime, final String label) {
final EditorInfo editorInfo = getCurrentInputEditorInfo();
mMediaInsertionController.insertMedia(getCurrentInputConnection(), editorInfo, uri, mime,
label, -1, editorInfo == null ? null : editorInfo.packageName,
mMediaInsertionController.getPreferredMediaMaxBytes(editorInfo));
}

public void insertExternalMedia(final MediaProviderItem item) {
final EditorInfo editorInfo = getCurrentInputEditorInfo();
mMediaInsertionController.insertMedia(getCurrentInputConnection(), editorInfo, item,
editorInfo == null ? null : editorInfo.packageName,
mMediaInsertionController.getPreferredMediaMaxBytes(editorInfo));
}

private long getPreferredMediaMaxBytes() {
return mMediaInsertionController.getPreferredMediaMaxBytes(getCurrentInputEditorInfo());
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null && EmojiSearchActivity.EMOJI_SEARCH_DONE_ACTION.equals(intent.getAction()) && ! isEmojiSearch()) {
Expand Down
Loading