mirror of
https://gitlab.com/android_translation_layer/android_translation_layer.git
synced 2025-04-28 20:27:58 +03:00
Merge branch 'media-provider' into 'master'
add a media ContentProvider See merge request android_translation_layer/android_translation_layer!156
This commit is contained in:
commit
01f038e5bb
31 changed files with 935 additions and 38 deletions
|
@ -113,6 +113,7 @@ libtranslationlayer_so = shared_library('translation_layer_main', [
|
|||
'src/api-impl-jni/AssetInputStream.c',
|
||||
'src/api-impl-jni/audio/android_media_AudioTrack.c',
|
||||
'src/api-impl-jni/audio/android_media_SoundPool.c',
|
||||
'src/api-impl-jni/content/android_content_ATLMediaContentProvider.c',
|
||||
'src/api-impl-jni/content/android_content_ClipboardManager.c',
|
||||
'src/api-impl-jni/content/android_content_ContentResolver.c',
|
||||
'src/api-impl-jni/content/android_content_Context.c',
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
#include <gtk/gtk.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "../generated_headers/android_content_ATLMediaContentProvider.h"
|
||||
#include "../defines.h"
|
||||
#include "../util.h"
|
||||
|
||||
extern GtkWindow *window;
|
||||
|
||||
static void file_dialog_callback(GObject *dialog, GAsyncResult *res, gpointer user_data) {
|
||||
GFile *file = gtk_file_dialog_open_finish(GTK_FILE_DIALOG(dialog), res, NULL);
|
||||
JNIEnv *env = get_jni_env();
|
||||
jobject this = (jobject) user_data;
|
||||
(*env)->CallVoidMethod(env, this, _METHOD(_CLASS(this), "setSelectedFile", "(Ljava/lang/String;)V"), _JSTRING(g_file_get_path(file)));
|
||||
g_object_unref(file);
|
||||
_UNREF(this);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_android_content_ATLMediaContentProvider_native_1open_1media_1folder(JNIEnv *env, jobject this)
|
||||
{
|
||||
GtkFileDialog *dialog = gtk_file_dialog_new();
|
||||
gtk_file_dialog_set_title(GTK_FILE_DIALOG(dialog), "Open Media Folder");
|
||||
gtk_file_dialog_set_modal(GTK_FILE_DIALOG(dialog), TRUE);
|
||||
gtk_file_dialog_open(dialog, window, NULL, file_dialog_callback, _REF(this));
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/* DO NOT EDIT THIS FILE - it is machine generated */
|
||||
#include <jni.h>
|
||||
/* Header for class android_content_ATLMediaContentProvider */
|
||||
|
||||
#ifndef _Included_android_content_ATLMediaContentProvider
|
||||
#define _Included_android_content_ATLMediaContentProvider
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/*
|
||||
* Class: android_content_ATLMediaContentProvider
|
||||
* Method: native_open_media_folder
|
||||
* Signature: ()V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_android_content_ATLMediaContentProvider_native_1open_1media_1folder
|
||||
(JNIEnv *, jobject);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
|
@ -39,6 +39,22 @@ JNIEXPORT void JNICALL Java_android_media_MediaPlayer_native_1setOnCompletionLis
|
|||
JNIEXPORT void JNICALL Java_android_media_MediaPlayer_native_1start
|
||||
(JNIEnv *, jclass, jlong);
|
||||
|
||||
/*
|
||||
* Class: android_media_MediaPlayer
|
||||
* Method: native_getDuration
|
||||
* Signature: (J)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_android_media_MediaPlayer_native_1getDuration
|
||||
(JNIEnv *, jclass, jlong);
|
||||
|
||||
/*
|
||||
* Class: android_media_MediaPlayer
|
||||
* Method: native_getCurrentPosition
|
||||
* Signature: (J)I
|
||||
*/
|
||||
JNIEXPORT jint JNICALL Java_android_media_MediaPlayer_native_1getCurrentPosition
|
||||
(JNIEnv *, jclass, jlong);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -56,3 +56,19 @@ JNIEXPORT void JNICALL Java_android_media_MediaPlayer_native_1start(JNIEnv *env,
|
|||
gtk_media_stream_set_volume(media_stream, 1.0);
|
||||
gtk_media_stream_play(media_stream);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_android_media_MediaPlayer_native_1getDuration(JNIEnv *env, jclass this, jlong media_stream_ptr)
|
||||
{
|
||||
GtkMediaStream *media_stream = _PTR(media_stream_ptr);
|
||||
|
||||
// convert from microseconds to milliseconds
|
||||
return gtk_media_stream_get_duration(media_stream) / 1000;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_android_media_MediaPlayer_native_1getCurrentPosition(JNIEnv *env, jclass this, jlong media_stream_ptr)
|
||||
{
|
||||
GtkMediaStream *media_stream = _PTR(media_stream_ptr);
|
||||
|
||||
// convert from microseconds to milliseconds
|
||||
return gtk_media_stream_get_timestamp(media_stream) / 1000;
|
||||
}
|
||||
|
|
|
@ -258,7 +258,7 @@ public class Activity extends ContextThemeWrapper implements Window.Callback, La
|
|||
protected void onSaveInstanceState(Bundle outState) {
|
||||
}
|
||||
|
||||
void onConfigurationChanged(Configuration newConfig) {
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
}
|
||||
|
||||
public void onLowMemory() {
|
||||
|
|
145
src/api-impl/android/content/ATLMediaContentProvider.java
Normal file
145
src/api-impl/android/content/ATLMediaContentProvider.java
Normal file
|
@ -0,0 +1,145 @@
|
|||
package android.content;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.database.MatrixCursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
public class ATLMediaContentProvider extends ContentProvider {
|
||||
|
||||
boolean waitingForFileChooser = false;
|
||||
File selectedFile = null;
|
||||
long timestamp = 0;
|
||||
|
||||
// called from native
|
||||
void setSelectedFile(String selectedFile) {
|
||||
this.selectedFile = new File(selectedFile);
|
||||
this.waitingForFileChooser = false;
|
||||
this.timestamp = System.currentTimeMillis();
|
||||
synchronized(this) {
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
private void openFileChooser() {
|
||||
if (!waitingForFileChooser) {
|
||||
waitingForFileChooser = true;
|
||||
new Handler(Looper.getMainLooper()).post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
native_open_media_folder();
|
||||
}
|
||||
});
|
||||
}
|
||||
synchronized(this) {
|
||||
try {
|
||||
while (waitingForFileChooser) {
|
||||
wait();
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
||||
// if we haven't selected a file, open the file chooser
|
||||
if (!"0".equals(uri.getLastPathSegment()) && timestamp + 1000 < System.currentTimeMillis()) {
|
||||
openFileChooser();
|
||||
}
|
||||
MatrixCursor cursor = new MatrixCursor(projection);
|
||||
Object[] row = new Object[projection.length];
|
||||
if (uri.getQueryParameter("distinct") != null) {
|
||||
for (int i = 0; i < projection.length; i++) {
|
||||
switch (projection[i]) {
|
||||
case "bucket_display_name":
|
||||
row[i] = "files";
|
||||
break;
|
||||
case "bucket_id":
|
||||
row[i] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < projection.length; i++) {
|
||||
switch (projection[i]) {
|
||||
case "_id":
|
||||
row[i] = 0;
|
||||
break;
|
||||
case "_data":
|
||||
case "title":
|
||||
row[i] = selectedFile;
|
||||
break;
|
||||
case "mime_type":
|
||||
row[i] = getType(uri);
|
||||
break;
|
||||
case "media_type":
|
||||
if (getType(uri).startsWith("image/"))
|
||||
row[i] = 1;
|
||||
else if (getType(uri).startsWith("audio/"))
|
||||
row[i] = 2;
|
||||
else if (getType(uri).startsWith("video/"))
|
||||
row[i] = 3;
|
||||
else
|
||||
row[i] = 0;
|
||||
break;
|
||||
case "date_modified":
|
||||
case "datetaken":
|
||||
row[i] = selectedFile.lastModified();
|
||||
break;
|
||||
case "orientation":
|
||||
row[i] = 0;
|
||||
break;
|
||||
case "_size":
|
||||
row[i] = selectedFile.length();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
cursor.addRow(row);
|
||||
return cursor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType(Uri uri) {
|
||||
try {
|
||||
return Files.probeContentType(selectedFile.toPath());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return "application/octet-stream";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
|
||||
return ParcelFileDescriptor.open(selectedFile, ParcelFileDescriptor.parseMode(mode));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri insert(Uri uri, ContentValues values) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Unimplemented method 'insert'");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Unimplemented method 'update'");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(Uri uri, String selection, String[] selectionArgs) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Unimplemented method 'delete'");
|
||||
}
|
||||
|
||||
private native void native_open_media_folder();
|
||||
}
|
|
@ -34,6 +34,7 @@ public abstract class ContentProvider {
|
|||
providers.put(provider_parsed.info.authority, provider);
|
||||
} catch(Exception e) { e.printStackTrace(); }
|
||||
}
|
||||
providers.put("media", new ATLMediaContentProvider());
|
||||
}
|
||||
|
||||
public boolean onCreate() {return false;}
|
||||
|
|
|
@ -395,7 +395,15 @@ public class Intent implements Parcelable {
|
|||
|
||||
public void setSourceBounds(Rect sourceBounds) {}
|
||||
|
||||
public Rect getSourceBounds() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setSelector(Intent selector) {}
|
||||
|
||||
public void setClipData(ClipData clip) {}
|
||||
|
||||
public String resolveType(Context context) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package android.content.pm;
|
||||
|
||||
import android.content.IntentFilter;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
public class ResolveInfo {
|
||||
public ActivityInfo activityInfo = new ActivityInfo();
|
||||
|
@ -8,6 +9,22 @@ public class ResolveInfo {
|
|||
public IntentFilter filter = new IntentFilter();
|
||||
public int priority = -500;
|
||||
|
||||
public Drawable loadIcon(PackageManager pm) {
|
||||
Drawable icon = activityInfo.loadIcon(pm);
|
||||
if (icon == null) {
|
||||
icon = new Drawable();
|
||||
}
|
||||
return icon;
|
||||
}
|
||||
|
||||
public CharSequence loadLabel(PackageManager pm) {
|
||||
CharSequence label = activityInfo.loadLabel(pm);
|
||||
if (label == null) {
|
||||
label = "fixme ResolveInfo.loadLabel";
|
||||
}
|
||||
return label;
|
||||
}
|
||||
|
||||
public static class DisplayNameComparator {
|
||||
|
||||
public DisplayNameComparator(PackageManager pm) {}
|
||||
|
|
|
@ -626,9 +626,9 @@ public class BitmapFactory {
|
|||
|
||||
Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeFileDescriptor");
|
||||
try {
|
||||
if (nativeIsSeekable(fd)) {
|
||||
bm = nativeDecodeFileDescriptor(fd, outPadding, opts);
|
||||
} else {
|
||||
//if (nativeIsSeekable(fd)) {
|
||||
// bm = nativeDecodeFileDescriptor(fd, outPadding, opts);
|
||||
//} else {
|
||||
FileInputStream fis = new FileInputStream(fd);
|
||||
try {
|
||||
bm = decodeStreamInternal(fis, outPadding, opts);
|
||||
|
@ -638,7 +638,7 @@ public class BitmapFactory {
|
|||
} catch (Throwable t) { /* ignore */
|
||||
}
|
||||
}
|
||||
}
|
||||
//}
|
||||
|
||||
if (bm == null && opts != null && opts.inBitmap != null) {
|
||||
throw new IllegalArgumentException("Problem decoding into existing bitmap");
|
||||
|
|
|
@ -474,6 +474,10 @@ public class Canvas {
|
|||
return save();
|
||||
}
|
||||
|
||||
public int saveLayer(RectF bounds, Paint paint) {
|
||||
return save();
|
||||
}
|
||||
|
||||
public void drawOval(RectF oval, Paint paint) {
|
||||
Log.w("Canvas", "STUB: drawOval");
|
||||
}
|
||||
|
|
|
@ -1,10 +1,20 @@
|
|||
package android.graphics.drawable;
|
||||
|
||||
public class TransitionDrawable extends Drawable {
|
||||
import android.graphics.Canvas;
|
||||
|
||||
public TransitionDrawable(Drawable[] layers) {}
|
||||
public class TransitionDrawable extends LayerDrawable {
|
||||
|
||||
public TransitionDrawable(Drawable[] layers) {
|
||||
super(layers);
|
||||
}
|
||||
|
||||
public void setCrossFadeEnabled(boolean enabled) {}
|
||||
|
||||
public void startTransition(int duration) {}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
// always draw the target drawable
|
||||
mLayerState.mChildren[1].mDrawable.draw(canvas);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,4 +56,8 @@ public class AudioManager {
|
|||
public boolean isStreamMute(int streamType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isMusicActive() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
37
src/api-impl/android/media/MediaMetadataRetriever.java
Normal file
37
src/api-impl/android/media/MediaMetadataRetriever.java
Normal file
|
@ -0,0 +1,37 @@
|
|||
package android.media;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
public class MediaMetadataRetriever {
|
||||
|
||||
private MediaPlayer mediaPlayer;
|
||||
|
||||
public void release() {
|
||||
if (mediaPlayer != null)
|
||||
mediaPlayer.release();
|
||||
}
|
||||
|
||||
public void setDataSource(Context context, Uri uri) {
|
||||
mediaPlayer = new MediaPlayer();
|
||||
mediaPlayer.setDataSource(uri.getPath());
|
||||
}
|
||||
|
||||
public void setDataSource(String path) {
|
||||
mediaPlayer = new MediaPlayer();
|
||||
mediaPlayer.setDataSource(path);
|
||||
}
|
||||
|
||||
public byte[] getEmbeddedPicture() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String extractMetadata(int key) {
|
||||
switch (key) {
|
||||
case 9/*METADATA_KEY_DURATION*/:
|
||||
return String.valueOf(mediaPlayer.getDuration());
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -68,8 +68,18 @@ public class MediaPlayer {
|
|||
|
||||
public void setVolume(float leftVolume, float rightVolume) {}
|
||||
|
||||
public int getDuration() {
|
||||
return native_getDuration(gtk_media_stream);
|
||||
}
|
||||
|
||||
public int getCurrentPosition() {
|
||||
return native_getCurrentPosition(gtk_media_stream);
|
||||
}
|
||||
|
||||
public static native void native_prepare(long gtk_media_stream);
|
||||
public native long native_setDataSource(String path);
|
||||
public static native void native_setOnCompletionListener(long gtk_media_stream, MediaPlayer.OnCompletionListener listener);
|
||||
public static native void native_start(long gtk_media_stream);
|
||||
public static native int native_getDuration(long gtk_media_stream);
|
||||
public static native int native_getCurrentPosition(long gtk_media_stream);
|
||||
}
|
||||
|
|
|
@ -601,19 +601,19 @@ public class ParcelFileDescriptor implements Closeable {
|
|||
*
|
||||
* @see #canDetectErrors()
|
||||
*/
|
||||
public int detachFd() { /*
|
||||
if (mWrapped != null) {
|
||||
return mWrapped.detachFd();
|
||||
} else {
|
||||
if (mClosed) {
|
||||
throw new IllegalStateException("Already closed");
|
||||
}
|
||||
final int fd = getFd();
|
||||
Parcel.clearFileDescriptor(mFd);
|
||||
writeCommStatusAndClose(Status.DETACHED, null);
|
||||
return fd;
|
||||
}*/
|
||||
return -1;
|
||||
public int detachFd() {
|
||||
if (mWrapped != null) {
|
||||
return mWrapped.detachFd();
|
||||
} else {
|
||||
if (mClosed) {
|
||||
throw new IllegalStateException("Already closed");
|
||||
}
|
||||
final int fd = getFd();
|
||||
// Parcel.clearFileDescriptor(mFd);
|
||||
mFd.setInt$(-1);
|
||||
writeCommStatusAndClose(Status.DETACHED, null);
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
package android.provider;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.database.Cursor;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.net.Uri;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
public class MediaStore {
|
||||
|
||||
|
@ -9,6 +16,43 @@ public class MediaStore {
|
|||
public static class Media {
|
||||
|
||||
public static final Uri EXTERNAL_CONTENT_URI = Uri.parse("content://media/external/images/media");
|
||||
public static final Uri INTERNAL_CONTENT_URI = Uri.parse("content://media/internal/images/media");
|
||||
}
|
||||
|
||||
public static class Thumbnails {
|
||||
|
||||
public static Cursor queryMiniThumbnail(ContentResolver contentResolver, long id, int kind, String[] projection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Bitmap getThumbnail(ContentResolver contentResolver, long imageId, long groupId, int kind, BitmapFactory.Options options) throws FileNotFoundException {
|
||||
ParcelFileDescriptor fd = contentResolver.openFileDescriptor(Media.EXTERNAL_CONTENT_URI.buildUpon().appendPath(String.valueOf(imageId)).build(), "r");
|
||||
return BitmapFactory.decodeFileDescriptor(fd.getFileDescriptor(), null, options);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Video {
|
||||
|
||||
public static class Media {
|
||||
|
||||
public static final Uri EXTERNAL_CONTENT_URI = Uri.parse("content://media/external/video/media");
|
||||
public static final Uri INTERNAL_CONTENT_URI = Uri.parse("content://media/internal/video/media");
|
||||
}
|
||||
}
|
||||
|
||||
public static class Audio {
|
||||
|
||||
public static class Media {
|
||||
|
||||
public static final Uri EXTERNAL_CONTENT_URI = Uri.parse("content://media/external/audio/media");
|
||||
}
|
||||
}
|
||||
|
||||
public static class Files {
|
||||
|
||||
public static Uri getContentUri(String type) {
|
||||
return Uri.parse("content://media/files/" + type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,6 @@ public class PhoneNumberUtils {
|
|||
}
|
||||
|
||||
public static boolean isGlobalPhoneNumber(String phoneNumber) {
|
||||
return phoneNumber.startsWith("+") || phoneNumber.startsWith("00");
|
||||
return phoneNumber != null && (phoneNumber.startsWith("+") || phoneNumber.startsWith("00"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,4 +74,6 @@ public interface MenuItem {
|
|||
public MenuItem setNumericShortcut(char numericChar);
|
||||
|
||||
public boolean expandActionView();
|
||||
|
||||
public boolean isActionViewExpanded();
|
||||
}
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
package android.view;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
public interface SubMenu extends Menu {
|
||||
|
||||
public MenuItem getItem();
|
||||
|
||||
public void clearHeader();
|
||||
|
||||
public SubMenu setIcon(Drawable icon);
|
||||
|
||||
}
|
||||
|
|
|
@ -654,5 +654,9 @@ public class ViewGroup extends View implements ViewParent, ViewManager {
|
|||
|
||||
public void requestChildFocus(View child, View focused) {}
|
||||
|
||||
public boolean getClipChildren() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public native boolean native_dispatchTouchEvent(long widget, MotionEvent event, double x, double y);
|
||||
}
|
||||
|
|
|
@ -88,4 +88,12 @@ public class ViewPropertyAnimator {
|
|||
}
|
||||
}, startDelay+duration);
|
||||
}
|
||||
|
||||
public ViewPropertyAnimator withEndAction(Runnable runnable) {
|
||||
return this;
|
||||
}
|
||||
|
||||
public ViewPropertyAnimator withStartAction(Runnable runnable) {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package android.view.animation;
|
||||
|
||||
import android.animation.TimeInterpolator;
|
||||
|
||||
public class AccelerateDecelerateInterpolator implements TimeInterpolator {
|
||||
public class AccelerateDecelerateInterpolator implements Interpolator {
|
||||
|
||||
@Override
|
||||
public float getInterpolation(float input) {
|
||||
|
|
|
@ -1,31 +1,513 @@
|
|||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.database.ContentObserver;
|
||||
import android.database.Cursor;
|
||||
import android.database.DataSetObserver;
|
||||
import android.os.Handler;
|
||||
import android.util.Log;
|
||||
import android.view.ContextThemeWrapper;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
public abstract class CursorAdapter extends BaseAdapter {
|
||||
/**
|
||||
* Adapter that exposes data from a {@link android.database.Cursor Cursor} to a
|
||||
* {@link android.widget.ListView ListView} widget.
|
||||
* <p>
|
||||
* The Cursor must include a column named "_id" or this class will not work.
|
||||
* Additionally, using {@link android.database.MergeCursor} with this class will
|
||||
* not work if the merged Cursors have overlapping values in their "_id"
|
||||
* columns.
|
||||
*/
|
||||
public abstract class CursorAdapter extends BaseAdapter implements Filterable {
|
||||
/**
|
||||
* This field should be made private, so it is hidden from the SDK.
|
||||
* {@hide}
|
||||
*/
|
||||
protected boolean mDataValid;
|
||||
/**
|
||||
* This field should be made private, so it is hidden from the SDK.
|
||||
* {@hide}
|
||||
*/
|
||||
protected boolean mAutoRequery;
|
||||
/**
|
||||
* This field should be made private, so it is hidden from the SDK.
|
||||
* {@hide}
|
||||
*/
|
||||
protected Cursor mCursor;
|
||||
/**
|
||||
* This field should be made private, so it is hidden from the SDK.
|
||||
* {@hide}
|
||||
*/
|
||||
protected Context mContext;
|
||||
/**
|
||||
* Context used for {@link #getDropDownView(int, View, ViewGroup)}.
|
||||
* {@hide}
|
||||
*/
|
||||
protected Context mDropDownContext;
|
||||
/**
|
||||
* This field should be made private, so it is hidden from the SDK.
|
||||
* {@hide}
|
||||
*/
|
||||
protected int mRowIDColumn;
|
||||
/**
|
||||
* This field should be made private, so it is hidden from the SDK.
|
||||
* {@hide}
|
||||
*/
|
||||
protected ChangeObserver mChangeObserver;
|
||||
/**
|
||||
* This field should be made private, so it is hidden from the SDK.
|
||||
* {@hide}
|
||||
*/
|
||||
protected DataSetObserver mDataSetObserver;
|
||||
/**
|
||||
* This field should be made private, so it is hidden from the SDK.
|
||||
* {@hide}
|
||||
*/
|
||||
// protected CursorFilter mCursorFilter;
|
||||
/**
|
||||
* This field should be made private, so it is hidden from the SDK.
|
||||
* {@hide}
|
||||
*/
|
||||
protected FilterQueryProvider mFilterQueryProvider;
|
||||
|
||||
private Cursor cursor;
|
||||
/**
|
||||
* If set the adapter will call requery() on the cursor whenever a content change
|
||||
* notification is delivered. Implies {@link #FLAG_REGISTER_CONTENT_OBSERVER}.
|
||||
*
|
||||
* @deprecated This option is discouraged, as it results in Cursor queries
|
||||
* being performed on the application's UI thread and thus can cause poor
|
||||
* responsiveness or even Application Not Responding errors. As an alternative,
|
||||
* use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
|
||||
*/
|
||||
@Deprecated
|
||||
public static final int FLAG_AUTO_REQUERY = 0x01;
|
||||
|
||||
public CursorAdapter(Context context, Cursor cursor, boolean autoRequery) {
|
||||
this.cursor = cursor;
|
||||
/**
|
||||
* If set the adapter will register a content observer on the cursor and will call
|
||||
* {@link #onContentChanged()} when a notification comes in. Be careful when
|
||||
* using this flag: you will need to unset the current Cursor from the adapter
|
||||
* to avoid leaks due to its registered observers. This flag is not needed
|
||||
* when using a CursorAdapter with a
|
||||
* {@link android.content.CursorLoader}.
|
||||
*/
|
||||
public static final int FLAG_REGISTER_CONTENT_OBSERVER = 0x02;
|
||||
|
||||
/**
|
||||
* Constructor that always enables auto-requery.
|
||||
*
|
||||
* @deprecated This option is discouraged, as it results in Cursor queries
|
||||
* being performed on the application's UI thread and thus can cause poor
|
||||
* responsiveness or even Application Not Responding errors. As an alternative,
|
||||
* use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
|
||||
*
|
||||
* @param c The cursor from which to get the data.
|
||||
* @param context The context
|
||||
*/
|
||||
@Deprecated
|
||||
public CursorAdapter(Context context, Cursor c) {
|
||||
init(context, c, FLAG_AUTO_REQUERY);
|
||||
}
|
||||
|
||||
public CursorAdapter(Context context, Cursor cursor, int flags) {
|
||||
this.cursor = cursor;
|
||||
/**
|
||||
* Constructor that allows control over auto-requery. It is recommended
|
||||
* you not use this, but instead {@link #CursorAdapter(Context, Cursor, int)}.
|
||||
* When using this constructor, {@link #FLAG_REGISTER_CONTENT_OBSERVER}
|
||||
* will always be set.
|
||||
*
|
||||
* @param c The cursor from which to get the data.
|
||||
* @param context The context
|
||||
* @param autoRequery If true the adapter will call requery() on the
|
||||
* cursor whenever it changes so the most recent
|
||||
* data is always displayed. Using true here is discouraged.
|
||||
*/
|
||||
public CursorAdapter(Context context, Cursor c, boolean autoRequery) {
|
||||
init(context, c, autoRequery ? FLAG_AUTO_REQUERY : FLAG_REGISTER_CONTENT_OBSERVER);
|
||||
}
|
||||
|
||||
public void changeCursor(Cursor cursor) {
|
||||
this.cursor = cursor;
|
||||
notifyDataSetChanged();
|
||||
/**
|
||||
* Recommended constructor.
|
||||
*
|
||||
* @param c The cursor from which to get the data.
|
||||
* @param context The context
|
||||
* @param flags Flags used to determine the behavior of the adapter; may
|
||||
* be any combination of {@link #FLAG_AUTO_REQUERY} and
|
||||
* {@link #FLAG_REGISTER_CONTENT_OBSERVER}.
|
||||
*/
|
||||
public CursorAdapter(Context context, Cursor c, int flags) {
|
||||
init(context, c, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Don't use this, use the normal constructor. This will
|
||||
* be removed in the future.
|
||||
*/
|
||||
@Deprecated
|
||||
protected void init(Context context, Cursor c, boolean autoRequery) {
|
||||
init(context, c, autoRequery ? FLAG_AUTO_REQUERY : FLAG_REGISTER_CONTENT_OBSERVER);
|
||||
}
|
||||
|
||||
void init(Context context, Cursor c, int flags) {
|
||||
if ((flags & FLAG_AUTO_REQUERY) == FLAG_AUTO_REQUERY) {
|
||||
flags |= FLAG_REGISTER_CONTENT_OBSERVER;
|
||||
mAutoRequery = true;
|
||||
} else {
|
||||
mAutoRequery = false;
|
||||
}
|
||||
boolean cursorPresent = c != null;
|
||||
mCursor = c;
|
||||
mDataValid = cursorPresent;
|
||||
mContext = context;
|
||||
mRowIDColumn = cursorPresent ? c.getColumnIndexOrThrow("_id") : -1;
|
||||
if ((flags & FLAG_REGISTER_CONTENT_OBSERVER) == FLAG_REGISTER_CONTENT_OBSERVER) {
|
||||
mChangeObserver = new ChangeObserver();
|
||||
mDataSetObserver = new MyDataSetObserver();
|
||||
} else {
|
||||
mChangeObserver = null;
|
||||
mDataSetObserver = null;
|
||||
}
|
||||
|
||||
if (cursorPresent) {
|
||||
if (mChangeObserver != null) c.registerContentObserver(mChangeObserver);
|
||||
if (mDataSetObserver != null) c.registerDataSetObserver(mDataSetObserver);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link Resources.Theme} against which drop-down views are
|
||||
* inflated.
|
||||
* <p>
|
||||
* By default, drop-down views are inflated against the theme of the
|
||||
* {@link Context} passed to the adapter's constructor.
|
||||
*
|
||||
* @param theme the theme against which to inflate drop-down views or
|
||||
* {@code null} to use the theme from the adapter's context
|
||||
* @see #newDropDownView(Context, Cursor, ViewGroup)
|
||||
*/
|
||||
public void setDropDownViewTheme(Resources.Theme theme) {
|
||||
if (theme == null) {
|
||||
mDropDownContext = null;
|
||||
} else if (theme == mContext.getTheme()) {
|
||||
mDropDownContext = mContext;
|
||||
} else {
|
||||
mDropDownContext = new ContextThemeWrapper(mContext, theme);
|
||||
}
|
||||
}
|
||||
|
||||
public Resources.Theme getDropDownViewTheme() {
|
||||
return mDropDownContext == null ? null : mDropDownContext.getTheme();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cursor.
|
||||
* @return the cursor.
|
||||
*/
|
||||
public Cursor getCursor() {
|
||||
return cursor;
|
||||
return mCursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see android.widget.ListAdapter#getCount()
|
||||
*/
|
||||
public int getCount() {
|
||||
if (mDataValid && mCursor != null) {
|
||||
return mCursor.getCount();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see android.widget.ListAdapter#getItem(int)
|
||||
*/
|
||||
public Object getItem(int position) {
|
||||
if (mDataValid && mCursor != null) {
|
||||
mCursor.moveToPosition(position);
|
||||
return mCursor;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see android.widget.ListAdapter#getItemId(int)
|
||||
*/
|
||||
public long getItemId(int position) {
|
||||
if (mDataValid && mCursor != null) {
|
||||
if (mCursor.moveToPosition(position)) {
|
||||
return mCursor.getLong(mRowIDColumn);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasStableIds() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see android.widget.ListAdapter#getView(int, View, ViewGroup)
|
||||
*/
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
if (!mDataValid) {
|
||||
throw new IllegalStateException("this should only be called when the cursor is valid");
|
||||
}
|
||||
if (!mCursor.moveToPosition(position)) {
|
||||
throw new IllegalStateException("couldn't move cursor to position " + position);
|
||||
}
|
||||
View v;
|
||||
if (convertView == null) {
|
||||
v = newView(mContext, mCursor, parent);
|
||||
} else {
|
||||
v = convertView;
|
||||
}
|
||||
bindView(v, mContext, mCursor);
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return cursor == null ? 0 : cursor.getCount();
|
||||
public View getDropDownView(int position, View convertView, ViewGroup parent) {
|
||||
if (mDataValid) {
|
||||
final Context context = mDropDownContext == null ? mContext : mDropDownContext;
|
||||
mCursor.moveToPosition(position);
|
||||
final View v;
|
||||
if (convertView == null) {
|
||||
v = newDropDownView(context, mCursor, parent);
|
||||
} else {
|
||||
v = convertView;
|
||||
}
|
||||
bindView(v, context, mCursor);
|
||||
return v;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a new view to hold the data pointed to by cursor.
|
||||
* @param context Interface to application's global information
|
||||
* @param cursor The cursor from which to get the data. The cursor is already
|
||||
* moved to the correct position.
|
||||
* @param parent The parent to which the new view is attached to
|
||||
* @return the newly created view.
|
||||
*/
|
||||
public abstract View newView(Context context, Cursor cursor, ViewGroup parent);
|
||||
|
||||
/**
|
||||
* Makes a new drop down view to hold the data pointed to by cursor.
|
||||
* @param context Interface to application's global information
|
||||
* @param cursor The cursor from which to get the data. The cursor is already
|
||||
* moved to the correct position.
|
||||
* @param parent The parent to which the new view is attached to
|
||||
* @return the newly created view.
|
||||
*/
|
||||
public View newDropDownView(Context context, Cursor cursor, ViewGroup parent) {
|
||||
return newView(context, cursor, parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind an existing view to the data pointed to by cursor
|
||||
* @param view Existing view, returned earlier by newView
|
||||
* @param context Interface to application's global information
|
||||
* @param cursor The cursor from which to get the data. The cursor is already
|
||||
* moved to the correct position.
|
||||
*/
|
||||
public abstract void bindView(View view, Context context, Cursor cursor);
|
||||
|
||||
/**
|
||||
* Change the underlying cursor to a new cursor. If there is an existing cursor it will be
|
||||
* closed.
|
||||
*
|
||||
* @param cursor The new cursor to be used
|
||||
*/
|
||||
public void changeCursor(Cursor cursor) {
|
||||
Cursor old = swapCursor(cursor);
|
||||
if (old != null) {
|
||||
old.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Swap in a new Cursor, returning the old Cursor. Unlike
|
||||
* {@link #changeCursor(Cursor)}, the returned old Cursor is <em>not</em>
|
||||
* closed.
|
||||
*
|
||||
* @param newCursor The new cursor to be used.
|
||||
* @return Returns the previously set Cursor, or null if there was not one.
|
||||
* If the given new Cursor is the same instance is the previously set
|
||||
* Cursor, null is also returned.
|
||||
*/
|
||||
public Cursor swapCursor(Cursor newCursor) {
|
||||
if (newCursor == mCursor) {
|
||||
return null;
|
||||
}
|
||||
Cursor oldCursor = mCursor;
|
||||
if (oldCursor != null) {
|
||||
if (mChangeObserver != null) oldCursor.unregisterContentObserver(mChangeObserver);
|
||||
if (mDataSetObserver != null) oldCursor.unregisterDataSetObserver(mDataSetObserver);
|
||||
}
|
||||
mCursor = newCursor;
|
||||
if (newCursor != null) {
|
||||
if (mChangeObserver != null) newCursor.registerContentObserver(mChangeObserver);
|
||||
if (mDataSetObserver != null) newCursor.registerDataSetObserver(mDataSetObserver);
|
||||
mRowIDColumn = newCursor.getColumnIndexOrThrow("_id");
|
||||
mDataValid = true;
|
||||
// notify the observers about the new cursor
|
||||
notifyDataSetChanged();
|
||||
} else {
|
||||
mRowIDColumn = -1;
|
||||
mDataValid = false;
|
||||
// notify the observers about the lack of a data set
|
||||
notifyDataSetInvalidated();
|
||||
}
|
||||
return oldCursor;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Converts the cursor into a CharSequence. Subclasses should override this
|
||||
* method to convert their results. The default implementation returns an
|
||||
* empty String for null values or the default String representation of
|
||||
* the value.</p>
|
||||
*
|
||||
* @param cursor the cursor to convert to a CharSequence
|
||||
* @return a CharSequence representing the value
|
||||
*/
|
||||
public CharSequence convertToString(Cursor cursor) {
|
||||
return cursor == null ? "" : cursor.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a query with the specified constraint. This query is requested
|
||||
* by the filter attached to this adapter.
|
||||
*
|
||||
* The query is provided by a
|
||||
* {@link android.widget.FilterQueryProvider}.
|
||||
* If no provider is specified, the current cursor is not filtered and returned.
|
||||
*
|
||||
* After this method returns the resulting cursor is passed to {@link #changeCursor(Cursor)}
|
||||
* and the previous cursor is closed.
|
||||
*
|
||||
* This method is always executed on a background thread, not on the
|
||||
* application's main thread (or UI thread.)
|
||||
*
|
||||
* Contract: when constraint is null or empty, the original results,
|
||||
* prior to any filtering, must be returned.
|
||||
*
|
||||
* @param constraint the constraint with which the query must be filtered
|
||||
*
|
||||
* @return a Cursor representing the results of the new query
|
||||
*
|
||||
* @see #getFilter()
|
||||
* @see #getFilterQueryProvider()
|
||||
* @see #setFilterQueryProvider(android.widget.FilterQueryProvider)
|
||||
*/
|
||||
// public Cursor runQueryOnBackgroundThread(CharSequence constraint) {
|
||||
// if (mFilterQueryProvider != null) {
|
||||
// return mFilterQueryProvider.runQuery(constraint);
|
||||
// }
|
||||
|
||||
// return mCursor;
|
||||
// }
|
||||
|
||||
// public Filter getFilter() {
|
||||
// if (mCursorFilter == null) {
|
||||
// mCursorFilter = new CursorFilter(this);
|
||||
// }
|
||||
// return mCursorFilter;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Returns the query filter provider used for filtering. When the
|
||||
* provider is null, no filtering occurs.
|
||||
*
|
||||
* @return the current filter query provider or null if it does not exist
|
||||
*
|
||||
* @see #setFilterQueryProvider(android.widget.FilterQueryProvider)
|
||||
* @see #runQueryOnBackgroundThread(CharSequence)
|
||||
*/
|
||||
public FilterQueryProvider getFilterQueryProvider() {
|
||||
return mFilterQueryProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the query filter provider used to filter the current Cursor.
|
||||
* The provider's
|
||||
* {@link android.widget.FilterQueryProvider#runQuery(CharSequence)}
|
||||
* method is invoked when filtering is requested by a client of
|
||||
* this adapter.
|
||||
*
|
||||
* @param filterQueryProvider the filter query provider or null to remove it
|
||||
*
|
||||
* @see #getFilterQueryProvider()
|
||||
* @see #runQueryOnBackgroundThread(CharSequence)
|
||||
*/
|
||||
public void setFilterQueryProvider(FilterQueryProvider filterQueryProvider) {
|
||||
mFilterQueryProvider = filterQueryProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the {@link ContentObserver} on the cursor receives a change notification.
|
||||
* The default implementation provides the auto-requery logic, but may be overridden by
|
||||
* sub classes.
|
||||
*
|
||||
* @see ContentObserver#onChange(boolean)
|
||||
*/
|
||||
protected void onContentChanged() {
|
||||
if (mAutoRequery && mCursor != null && !mCursor.isClosed()) {
|
||||
if (false) Log.v("Cursor", "Auto requerying " + mCursor + " due to update");
|
||||
mDataValid = mCursor.requery();
|
||||
}
|
||||
}
|
||||
|
||||
private class ChangeObserver extends ContentObserver {
|
||||
public ChangeObserver() {
|
||||
super(new Handler());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deliverSelfNotifications() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
onContentChanged();
|
||||
}
|
||||
}
|
||||
|
||||
private class MyDataSetObserver extends DataSetObserver {
|
||||
@Override
|
||||
public void onChanged() {
|
||||
mDataValid = true;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInvalidated() {
|
||||
mDataValid = false;
|
||||
notifyDataSetInvalidated();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -163,7 +163,10 @@ public class ImageView extends View {
|
|||
public final void setColorFilter(int color, PorterDuff.Mode mode) {}
|
||||
|
||||
public void setImageTintList(ColorStateList tint) {
|
||||
colorFilter = new PorterDuffColorFilter(tint.getDefaultColor(), PorterDuff.Mode.SRC_IN);
|
||||
if (tint == null)
|
||||
colorFilter = null;
|
||||
else
|
||||
colorFilter = new PorterDuffColorFilter(tint.getDefaultColor(), PorterDuff.Mode.SRC_IN);
|
||||
setImageDrawable(drawable);
|
||||
}
|
||||
|
||||
|
@ -191,6 +194,10 @@ public class ImageView extends View {
|
|||
|
||||
public void setColorFilter(ColorFilter cf) {}
|
||||
|
||||
public Matrix getImageMatrix() {
|
||||
return Matrix.IDENTITY_MATRIX;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected native long native_constructor(Context context, AttributeSet attrs);
|
||||
protected native void native_setDrawable(long widget, long paintable);
|
||||
|
|
|
@ -335,6 +335,12 @@ public class PopupMenu {
|
|||
throw new UnsupportedOperationException("Unimplemented method 'clearHeader'");
|
||||
}
|
||||
|
||||
@Override
|
||||
public SubMenu setIcon(Drawable icon) {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Unimplemented method 'setIcon'");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class MenuItemImpl implements MenuItem {
|
||||
|
@ -549,6 +555,12 @@ public class PopupMenu {
|
|||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Unimplemented method 'expandActionView'");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActionViewExpanded() {
|
||||
// TODO Auto-generated method stub
|
||||
throw new UnsupportedOperationException("Unimplemented method 'isActionViewExpanded'");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,11 +19,12 @@ public class ProgressBar extends View {
|
|||
super(context, attrs, defStyle);
|
||||
haveCustomMeasure = false;
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.ProgressBar, defStyle, 0);
|
||||
setIndeterminateDrawable(a.getDrawable(com.android.internal.R.styleable.ProgressBar_indeterminateDrawable));
|
||||
setProgressDrawable(a.getDrawable(com.android.internal.R.styleable.ProgressBar_progressDrawable));
|
||||
setIndeterminate(a.getBoolean(com.android.internal.R.styleable.ProgressBar_indeterminate, false));
|
||||
if (a.getBoolean(com.android.internal.R.styleable.ProgressBar_indeterminateOnly, false)) {
|
||||
setIndeterminate(true);
|
||||
}
|
||||
setIndeterminateDrawable(a.getDrawable(com.android.internal.R.styleable.ProgressBar_indeterminateDrawable));
|
||||
/* FIXME hack: NewPipe expects this to not be null, but for some reason it is */
|
||||
if(indeterminateDrawable == null)
|
||||
indeterminateDrawable = new Drawable() {
|
||||
|
|
|
@ -1,4 +1,20 @@
|
|||
package android.widget;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
|
||||
public class RemoteViews {
|
||||
|
||||
public RemoteViews(String packageName, int layoutId) {}
|
||||
|
||||
public void setProgressBar(int viewId, int max, int progress, boolean indeterminate) {}
|
||||
|
||||
public void setTextViewText(int viewId, CharSequence text) {}
|
||||
|
||||
public void setImageViewResource(int viewId, int resId) {}
|
||||
|
||||
public void setContentDescription(int viewId, CharSequence text) {}
|
||||
|
||||
public void setOnClickPendingIntent(int viewId, PendingIntent pendingIntent) {}
|
||||
|
||||
public void setViewVisibility(int viewId, int visibility) {}
|
||||
}
|
||||
|
|
|
@ -403,4 +403,6 @@ public class TextView extends View {
|
|||
public void setShadowLayer(float radius, float dx, float dy, int color) {}
|
||||
|
||||
public void setBreakStrategy(int strategy) {}
|
||||
|
||||
public void clearComposingText() {}
|
||||
}
|
||||
|
|
|
@ -76,6 +76,7 @@ srcs = [
|
|||
'android/bluetooth/le/ScanCallback.java',
|
||||
'android/content/ActivityNotFoundException.java',
|
||||
'android/content/AsyncQueryHandler.java',
|
||||
'android/content/ATLMediaContentProvider.java',
|
||||
'android/content/BroadcastReceiver.java',
|
||||
'android/content/ClipboardManager.java',
|
||||
'android/content/ClipData.java',
|
||||
|
@ -278,6 +279,7 @@ srcs = [
|
|||
'android/media/MediaDescription.java',
|
||||
'android/media/MediaFormat.java',
|
||||
'android/media/MediaMetadata.java',
|
||||
'android/media/MediaMetadataRetriever.java',
|
||||
'android/media/MediaPlayer.java',
|
||||
'android/media/MediaRouter.java',
|
||||
'android/media/Ringtone.java',
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue