adb version.| Assert | C00000 |
| Debug | 41BB2E |
| Error | FF6B68 |
| Info | F5F550 |
| Verbose | BBBBBB |
| Warning | F4AC40 |
scrcpy qui nous permettra de partager l'affichage de notre téléphone.
C:\tools par exemple) ce qui donnera dans notre cas :
C:\tools\scrcpy-win64-v1.16.
scrcpy.and_fullscreen_immersive, à l'extérieur d'une méthode).
and_pip, à l'extérieur d'une méthode).
<manifest ... >
<application ... >
<activity android:name=".ExampleActivity" />
...
</application ... >
...
</manifest >
Layout.OnClickListener.
// Create the text message with a string
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.setType("text/plain");
sendIntent.putExtra(Intent.EXTRA_TEXT, "Un texte à partager");
// Start the activity
startActivity(sendIntent);
// Create the text message with a string
val sendIntent = Intent()
with(sendIntent){
action = Intent.ACTION_SEND
type = "text/plain"
putExtra(Intent.EXTRA_TEXT, "Un texte à partager")
}
// Start the activity
startActivity(sendIntent)
<activity android:name=".ReceiveTextActivity">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
Bundle extras = getIntent().getExtras();
String receivedText = (extras != null) ? extras.getString(Intent.EXTRA_TEXT) : "";
Log.d(TAG, "onCreate received text = " + receivedText);
val receivedText = intent.extras?.getString(Intent.EXTRA_TEXT) ?: ""
Log.d(TAG, "onCreate received text = $receivedText")
sendIntent.setAction("aaa");
Comment y remédier ?
Il est préférable de vérifier que l'Intent pourra être récupéré avec le code suivant :
// Verify that the intent will resolve to an activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
startActivity(sendIntent);
}
Comment faire si l'on veut proposer le choix à chaque fois ?
On va forcer le lancement du chooser, avec le code suivant :
// Always use string resources for UI text.
// This says something like "Share this photo with"
String title = getResources().getString(R.string.chooser_title);
// Create intent to show the chooser dialog
Intent chooser = Intent.createChooser(sendIntent, title);
// Verify the original intent will resolve to at least one activity
if (sendIntent.resolveActivity(getPackageManager()) != null) {
startActivity(chooser);
}
Comment faire si l'on veut proposer le choix à chaque fois ?
On va forcer le lancement du chooser, avec le code suivant :
// Always use string resources for UI text.
// This says something like "Share this photo with"
val title = resources.getString(R.string.chooser_title)
// Create intent to show the chooser dialog
val chooser = Intent.createChooser(sendIntent, title)
// Verify the original intent will resolve to at least one activity
if (sendIntent.resolveActivity(packageManager) != null) {
startActivity(chooser)
}
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="http"/>
<data android:host="test.link.com"/>
</intent-filter>
Essayons de cliquer sur le lien suivant : Ouvrir application.

and_lifecycle_activity, dans notre classe de l'Activity.and_tag, juste après la déclaration de notre classe, avant la déclaration des
méthodes.

layout-sw600dp par layout-land.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment android:name="com.example.news.ArticleListFragment"
android:id="@+id/list"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<fragment android:name="com.example.news.ArticleReaderFragment"
android:id="@+id/viewer"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>
FragmentManager :
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Qui permet par la suite de gérer dynamiquement les fragments :
ExampleFragment fragment = new ExampleFragment();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();













adb tcpip 5555
Puis il suffit de se connecter sur l'adresse IP du téléphone, par exemple :
adb connect 192.168.1.99
@echo off
echo Activate tcp mode
adb -d tcpip 5555
REM Sleep for 1 seconds
echo Wait a bit
ping -n 2 127.0.0.1>nul
echo Find IP
FOR /F "tokens=2 skip=1" %%A IN ('adb -d shell ip -f inet addr show wlan0') DO (
FOR /F "tokens=1 delims=/" %%B IN ("%%A") DO (
echo "Connecting to %%B"
adb connect %%B
)
goto :eof
)
:eof










<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_cloud"
android:tint="@color/colorAccent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />

CALL_PHONE.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.phonecall">
<uses-permission android:name="android.permission.CALL_PHONE"/>
<application ...>
...
</application>
</manifest>
if (ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.CALL_PHONE)
!= PackageManager.PERMISSION_GRANTED) {
// Permission is not granted
}
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.CALL_PHONE)
!= PackageManager.PERMISSION_GRANTED) {
// Permission is not granted
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.CALL_PHONE)) {
// Show an explanation to the user *asynchronously* -- don't block
// this thread waiting for the user's response! After the user
// sees the explanation, try again to request the permission.
} else {
// No explanation needed; request the permission
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.CALL_PHONE},
MY_PERMISSIONS_REQUEST_CALL_PHONE);
// MY_PERMISSIONS_REQUEST_CALL_PHONE is an
// app-defined int constant. The callback method gets the
// result of the request.
}
} else {
// Permission has already been granted
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_CALL_PHONE: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted, yay! Do the job you need to do.
} else {
// permission denied, boo! Disable the functionality that depends on this permission.
}
return;
}
// other 'case' lines to check for other
// permissions this app might request.
}
}
and_permission_single.actionToBeCalled().@SuppressLint("MissingPermission") sur cette méthode pour supprimer le warning.callAction().
String url = "tel:"+"0612345678";
Intent callIntent = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
startActivity(callIntent);
android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
applicationId "com.example.services"
minSdkVersion 15
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
debug qui est définis. Un second type release est aussi définis.
flavors.
buildTypes {
// ...
}
flavorDimensions "version"
productFlavors {
demo {
dimension "version"
}
full {
dimension "version"
}
}






ext {
appcompatVersion = '1.1.0'
constraintLayoutVersion = '1.1.3'
junitVersion = '4.12'
runnerVersion = '1.2.0'
espressoVersion = '3.2.0'
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "androidx.appcompat:appcompat:$appcompatVersion"
implementation "androidx.constraintlayout:constraintlayout:$constraintLayoutVersion"
testImplementation "junit:junit:$junitVersion"
androidTestImplementation "androidx.test:runner:$runnerVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
}


lint.xml à la racine du projet.@SuppressLint.
<?xml version="1.0" encoding="UTF-8"?>
<lint>
<!-- Disable the given check in this project -->
<issue id="IconMissingDensityFolder" severity="ignore" />
<!-- Ignore the ObsoleteLayoutParam issue in the specified files -->
<issue id="ObsoleteLayoutParam">
<ignore path="res/layout/activation.xml" />
<ignore path="res/layout-xlarge/activation.xml" />
</issue>
<!-- Ignore the UselessLeaf issue in the specified file -->
<issue id="UselessLeaf">
<ignore path="res/layout/main.xml" />
</issue>
<!-- Change the severity of hardcoded strings to "error" -->
<issue id="HardcodedText" severity="error" />
</lint>
maven { url "https://jitpack.io" }
(Page du projet).
Can’t find referenced class org.sl4j ne semble plus être d'actualité.strings.xml et son utilisation pour internationaliser notre application.
com.google.android.material.button.MaterialButton.
and_xml_material_design_edittext.
TextView, All Attributes,
fontFamilly
et sélectionnons par exemple smokum.
SeekBar.colors.xmldimens.xmlstyles.xmlcolors.xml afin de regrouper ce type de ressources.
dimens.xml nous permet de déclarer les différentes tailles (taille du texte, marge, ...) afin de regrouper ce type de ressources.
<style name="DialogTitle" parent="AppTheme">
<item name="android:padding">@dimen/dialog_title_text_padding</item>
<item name="android:textSize">@dimen/dialog_title_text_size</item>
<item name="android:textStyle">bold</item>
</style>
Widget.
CharSequence widgetText = context.getString(R.string.appwidget_text);
Par :
Date current = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMM yyy");
String widgetText = dateFormat.format(current);

TextView tvText;
Définissons le code du bouton :
tvText = findViewById(R.id.text);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
tvText.setVisibility(tvText.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE );
}
});
Testons.
android:animateLayoutChanges="true"
Testons de nouveau.
background.xml :
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:angle="90"
android:endColor="#FFF"
android:startColor="#CCC" />
</shape>
Ajoutons le en background de notre layout principal.
toolsandroid par le préfix tools ce qui impactera seulement l'affichage dans l'IDE, mais
sera ignoré dans
l'application. Testons par exemple en changeant la visibilité d'un élément :
tools:visibility="gone"



@Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
// Add a marker in Startup Marseille and move the camera
LatLng startupMarseille = new LatLng(43.291590, 5.378210);
mMap.addMarker(new MarkerOptions().position(startupMarseille).title("Marker at Startup Marseille."));
mMap.moveCamera(CameraUpdateFactory.zoomTo(19));
mMap.moveCamera(CameraUpdateFactory.newLatLng(startupMarseille));
}
implementation 'com.google.android.gms:play-services-location:17.0.0'
Définir un attribut de type Marker pour garder une référence sur le marker de la position courante :
private Marker currentPosition;
FusedLocationProviderClient client = LocationServices.getFusedLocationProviderClient(this);
// Get the last known location
client.getLastLocation().addOnCompleteListener(this, new OnCompleteListener<Location>() {
@Override
public void onComplete(@NonNull Task<Location> task) {
// TODO
}
});
LatLng currentLocation = new LatLng(task.getResult().getLatitude(), task.getResult().getLongitude());
currentPosition = mMap.addMarker(new MarkerOptions().position(currentLocation).title("Current position").icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN)));
com.google.android.gms.tasks.RuntimeExecutionException: java.lang.SecurityException: Client must have ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission to perform any location operations.
Comment la corriger ?
int color = Color.RED;
Drawable background = rootView.getBackground();
if (background instanceof ColorDrawable)
color = ((ColorDrawable) background).getColor();
ImageView à notre projet.
ImageView imageView = findViewById(R.id.image);
Picasso.get().setLoggingEnabled(true);
Picasso.get().setIndicatorsEnabled(true);
Picasso.get().load("https://upload.wikimedia.org/wikipedia/commons/d/da/Internet2.jpg").into(imageView);
Glide.with(this).load("https://upload.wikimedia.org/wikipedia/commons/d/da/Internet2.jpg").into(imageView);
Canvas. Pour cela nous allons suivre les étapes suivantes :
File / New / Ui Component / Custom View

Paint.translate.rotate.setColor.Path.drawArc.
private int mAngle = 0;
Et le setter associé :
public void setmAngle(int mAngle) {
this.mAngle = mAngle;
invalidate();
}
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int cx = getWidth() / 2;
int cy = getHeight() / 2;
Paint paint = new Paint();
// Définir la flèche
Path northPath = new Path();
northPath.moveTo(0, -cy);
northPath.lineTo(-10, 0);
northPath.lineTo(10, 0);
northPath.close();
// Dessiner le cercle de fond
// Définir le rectangle contenant le cercle que l'on va dessiner
RectF pitchOval = new RectF(0, 0, getWidth(), getHeight());
paint.setColor(Color.BLACK);
canvas.drawArc(pitchOval, 0, 360, false, paint);
canvas.translate(cx, cy);
canvas.rotate(mAngle);
paint.setColor(Color.RED);
canvas.drawPath(northPath, paint);
}
public class MyView extends View {
private int mAngle = 0;
public MyView(Context context) {
super(context);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int cx = getWidth() / 2;
int cy = getHeight() / 2;
Paint paint = new Paint();
// Définir la flèche
Path northPath = new Path();
northPath.moveTo(0, -cy);
northPath.lineTo(-10, 0);
northPath.lineTo(10, 0);
northPath.close();
// Dessiner le cercle de fond
// Définir le rectangle contenant le cercle que l'on va dessiner
RectF pitchOval = new RectF(0, 0, getWidth(), getHeight());
paint.setColor(Color.BLACK);
canvas.drawArc(pitchOval, 0, 360, false, paint);
canvas.translate(cx, cy);
canvas.rotate(mAngle);
paint.setColor(Color.RED);
canvas.drawPath(northPath, paint);
}
public void setmAngle(int mAngle) {
this.mAngle = mAngle;
invalidate();
}
}
SensorManager sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
List<Sensor> liste = sensorManager.getSensorList(Sensor.TYPE_ALL);
if (BuildConfig.DEBUG) {
for (Sensor currentSensor : liste) {
Log.d(TAG, "onCreate sensor " + currentSensor);
}
}
val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
var liste = sensorManager.getSensorList(Sensor.TYPE_ALL)
if (BuildConfig.DEBUG) {
for (currentSensor in liste) {
Log.d(TAG, "onCreate sensor $currentSensor")
}
}
private Sensor mProximitySensor = null; // En attribut de classe.
mProximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
var mProximitySensor:Sensor? = null // En attribut de classe.
mProximitySensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)
private SensorManager mSensorManager = null; // En attribut de classe.
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
Puis nous nous enregistrons sur les changements uniquement quand l'application est active :
@Override
protected void onResume() {
super.onResume();
mSensorManager.registerListener(mSensorEventListener, mProximitySensor, SensorManager.SENSOR_DELAY_NORMAL);
}
@Override
protected void onPause() {
super.onPause();
mSensorManager.unregisterListener(mSensorEventListener, mProximitySensor);
}
final SensorEventListener mSensorEventListener = new SensorEventListener() {
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// Que faire en cas de changement de précision ?
}
public void onSensorChanged(SensorEvent sensorEvent) {
if(BuildConfig.DEBUG){
Log.d(TAG, "onSensorChanged " + sensorEvent.values.length);
Log.d(TAG, "onSensorChanged " + sensorEvent.values[0]);
}
}
};
Il ne nous reste plus qu'à implémenter notre code fonctionnel.
var mSensorManager: SensorManager? = null // En attribut de classe.
mSensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
Puis nous nous enregistrons sur les changements uniquement quand l'application est active :
override fun onResume() {
super.onResume()
mSensorManager?.registerListener(
mSensorEventListener,
mProximitySensor,
SensorManager.SENSOR_DELAY_NORMAL
)
}
override fun onPause() {
super.onPause()
mSensorManager?.unregisterListener(mSensorEventListener, mProximitySensor)
}
val mSensorEventListener: SensorEventListener = object : SensorEventListener {
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {
// Que faire en cas de changement de précision ?
}
override fun onSensorChanged(sensorEvent: SensorEvent) {
if (BuildConfig.DEBUG) {
Log.d(TAG, "onSensorChanged " + sensorEvent.values.size)
Log.d(TAG, "onSensorChanged " + sensorEvent.values[0])
}
}
}
Il ne nous reste plus qu'à implémenter notre code fonctionnel.
Uri media = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
String[] projection = { MediaStore.Audio.Media._ID, // 0
MediaStore.Audio.Media.ARTIST, // 1
MediaStore.Audio.Media.TITLE, // 2
MediaStore.Audio.Media.ALBUM_ID, // 3
MediaStore.Audio.Media.ALBUM, // 4
MediaStore.Audio.Media.DATA, // 5
MediaStore.Audio.Media.DISPLAY_NAME, // 6
MediaStore.Audio.Media.DURATION }; // 7
String selection = MediaStore.Audio.Media.IS_MUSIC + " != 0";
Cursor cursor = getContentResolver().query(media,projection,selection,null,null);
while(cursor.moveToNext()){
if(BuildConfig.DEBUG){
Log.d(TAG, "onCreate " +
cursor.getString(0) + " " +
cursor.getString(1) + " " +
cursor.getString(2) + " " +
cursor.getString(3) + " " +
cursor.getString(4) + " " +
cursor.getString(5) + " " +
cursor.getString(6) + " " +
cursor.getString(7) + " ");
}
}
AndroidManifest.xml la permission :
<uses-permission android:name="android.permission.READ_USER_DICTIONARY"/>
UserDictionary.
// A "projection" defines the columns that will be returned for each row
String[] projection = {
UserDictionary.Words._ID, // Contract class constant for the _ID column name
UserDictionary.Words.WORD, // Contract class constant for the word column name
UserDictionary.Words.LOCALE // Contract class constant for the locale column name
};
// Defines a string to contain the selection clause
String selectionClause = null;
// Initializes an array to contain selection arguments
String[] selectionArgs = null;
// Defines a string to order the result
String sortOrder = null;
// Does a query against the table and returns a Cursor object
Cursor mCursor = getContentResolver().query(
UserDictionary.Words.CONTENT_URI, // The content URI of the words table
projection, // The columns to return for each row
selectionClause, // Either null, or the word the user entered
selectionArgs, // Either empty, or the string the user entered
sortOrder); // The sort order for the returned rows
// Some providers return null if an error occurs, others throw an exception
if (null == mCursor) {
/*
* Insert code here to handle the error. Be sure not to use the cursor! You may want to
* call android.util.Log.e() to log this error.
*
*/
if (BuildConfig.DEBUG) {
Log.d(TAG, "onCreate CURSOR NULL");
}
// If the Cursor is empty, the provider found no matches
} else if (mCursor.getCount() < 1) {
/*
* Insert code here to notify the user that the search was unsuccessful. This isn't necessarily
* an error. You may want to offer the user the option to insert a new row, or re-type the
* search term.
*/
if (BuildConfig.DEBUG) {
Log.d(TAG, "onCreate CURSOR EMPTY");
}
} else {
// Insert code here to do something with the results
try {
while (mCursor.moveToNext()) {
if (BuildConfig.DEBUG) {
Log.d(TAG, "onCreate " +
mCursor.getLong(0) + " " +
mCursor.getString(1) + " " +
mCursor.getString(2) + " "
);
}
}
} finally {
mCursor.close();
}
}
// Defines a new Uri object that receives the result of the insertion
Uri newUri;
// Defines an object to contain the new values to insert
ContentValues newValues = new ContentValues();
/*
* Sets the values of each column and inserts the word. The arguments to the "put"
* method are "column name" and "value"
*/
newValues.put(UserDictionary.Words.APP_ID, "example.user");
newValues.put(UserDictionary.Words.LOCALE, "en_US");
newValues.put(UserDictionary.Words.WORD, "insert");
newValues.put(UserDictionary.Words.FREQUENCY, "100");
newUri = getContentResolver().insert(
UserDictionary.Words.CONTENT_URI, // the user dictionary content URI
newValues // the values to insert
);
if (BuildConfig.DEBUG) {
Log.d(TAG, "onCreate " + newUri);
}
// Defines an object to contain the updated values
ContentValues updateValues = new ContentValues();
// Defines selection criteria for the rows you want to update
String selectionClause = UserDictionary.Words.LOCALE + " LIKE ?";
String[] selectionArgs = {"en_%"};
// Defines a variable to contain the number of updated rows
int rowsUpdated = 0;
/*
* Sets the updated value and updates the selected words.
*/
updateValues.putNull(UserDictionary.Words.LOCALE);
rowsUpdated = getContentResolver().update(
UserDictionary.Words.CONTENT_URI, // the user dictionary content URI
updateValues, // the columns to update
selectionClause, // the column to select on
selectionArgs // the value to compare to
);
// Defines selection criteria for the rows you want to delete
String selectionClause = UserDictionary.Words.WORD + " LIKE ?";
String[] selectionArgs = {"%insert%"};
// Defines a variable to contain the number of rows deleted
int rowsDeleted = 0;
// Deletes the words that match the selection criteria
rowsDeleted = getContentResolver().delete(
UserDictionary.Words.CONTENT_URI, // the user dictionary content URI
selectionClause, // the column to select on
selectionArgs // the value to compare to
);
ContentProvider.query.ContentProviderClient la bonne réaction.
query doit retourner un Cursor, implémentons rapidement la création d'un Cursor contenant des Users.
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
MatrixCursor cursor = new MatrixCursor(new String[]{"name", "firstname"});
cursor.newRow()
.add("name", "SALAUN")
.add("firstname", "Tristan");
cursor.newRow()
.add("name", "SNOW")
.add("firstname", "John");
return cursor;
}
ContentProviderClient, et ajoutons le code pour interroger notre nouveau ContentProvider :
Cursor cursor = getContentResolver().query(Uri.parse("content://com.exemple.contentprovider.provider"), null, null, null, null);
if(cursor.moveToFirst()) {
StringBuilder strBuild=new StringBuilder();
while (!cursor.isAfterLast()) {
strBuild.append("\n"+cursor.getString(0)+ "-"+ cursor.getString(1));
cursor.moveToNext();
}
if(BuildConfig.DEBUG){
Log.d(TAG, "onCreate " + strBuild);
}
}
and_db_room_00_documentation et écrire les classes pour gérer ma base de donnée. La base de notre
Content Provider sera l'entity suivante :
public class UserEntity {
public String name;
public String firstname;
}
import android.net.Uri;
import android.provider.BaseColumns;
public final class UserContract {
// The Authority
public static final String AUTHORITY = "com.exemple.contentprovider.provider";
// The path to the data… and explain
public static final String PATH_TO_DATA = "users"; //Vous pouvez déclarer plusieurs paths (les paths utilisent les /)
public interface MyDatas extends BaseColumns {
// The URI and explain, with example if you want
public static final Uri CONTENT_URI = Uri.parse("content://" + UserContract.AUTHORITY + "/" + UserContract.PATH_TO_DATA);
// My Column ID and the associated explanation for end-users
public static final String KEY_COL_ID = _ID; // Mandatory
// My Column Name and the associated explanation for end-users
public static final String KEY_COL_NAME = "name";
// My Column First Name and the associated explanation for end-users
public static final String KEY_COL_FIRSTNAME = "firstName";
// The index of the column ID
public static final int ID_COLUMN = 1;
// The index of the column NAME
public static final int NAME_COLUMN = 2;
// The index of the column FIRST NAME
public static final int FIRSTNAME_COLUMN = 3;
}
}
afin de récupérer une instance de la base de donnée :
public boolean onCreate() {
Context context = getContext();
userDao = AppDatabase.getDatabase(context).userDao();
if (userDao != null) {
return true;
}
return false;
}
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
return userDao.getCursorAll();
}
public Uri insert(Uri uri, ContentValues values) {
UserEntity currentUserEntity = new UserEntity();
if (values.containsKey(UserContract.MyDatas.KEY_COL_FIRSTNAME)) {
currentUserEntity.firstname = values.getAsString(UserContract.MyDatas.KEY_COL_FIRSTNAME);
}
if (values.containsKey(UserContract.MyDatas.KEY_COL_NAME)) {
currentUserEntity.name = values.getAsString(UserContract.MyDatas.KEY_COL_NAME);
}
long rowID = userDao.insert(currentUserEntity);
if (rowID > 0) {
Uri _uri = ContentUris.withAppendedId(UserContract.MyDatas.CONTENT_URI, rowID);
getContext().getContentResolver().notifyChange(_uri, null);
return _uri;
}
throw new SQLiteException("Failed to add a record into " + uri);
}
MainActivity :
for (int i = 0; i < 10; i++) {
if (BuildConfig.DEBUG) {
Log.d(TAG, "run background " + i);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Que remarquons nous ?Plaçons maintenant le code dans notre service, que remarquons nous ?
and_intent_set_task.
monkeyrunner.
adb shell monkey -p your.package.name -v 500
monkeyrunnermonkeyrunner qui va installer
l'application, réaliser des actions (tout en effectuant des captures d'écran), pour enfin effacer l'application testée.screenshoot_app.py :
# -*- coding: utf-8 -*-
# https://developer.android.com/studio/test/monkeyrunner
# https://medium.com/@soonsantos/guide-to-run-monkeyrunner-e9363f36bca4
# Imports the monkeyrunner modules used by this program
from com.android.monkeyrunner import MonkeyRunner, MonkeyDevice
# Connects to the current device, returning a MonkeyDevice object
device = MonkeyRunner.waitForConnection()
MonkeyRunner.alert(u"\n\nMonkeyRunnerScript\nMerci de vérifier que :\n\
\t - Le téléphone est bien installé.\n\
\n\n\n\n\n\
Appuyez sur 'Continuer' POUR REPENDRE L'EXECUTION...","MonkeyRunner","Continuer");
print("Start")
# Presses the Home button
device.press('KEYCODE_HOME',MonkeyDevice.DOWN_AND_UP)
# sets a variable with the package's internal name
package = 'fr.salaun.tristan.todelete'
# sets a variable with the name of an Activity in the package
activity = 'fr.salaun.tristan.todelete.MainActivity'
apk_path = device.shell('pm path ' + package)
if apk_path.startswith('package:'):
print "XXXYY already installed."
else:
print "XXXYY app is not installed, installing APKs..."
device.installPackage('D:/path to apk/yourapp.apk')
# sets the name of the component to start
runComponent = package + '/' + activity
# Runs the component
device.startActivity(component=runComponent)
# Wait a bit
MonkeyRunner.sleep(1)
# Takes a screenshot
print("Take screenshoot of screen")
result = device.takeSnapshot()
# Writes the screenshot to a file
result.writeToFile('screenshoot.png','png')
# ==================================================
# Go out of the application
device.press("KEYCODE_BACK", MonkeyDevice.DOWN_AND_UP)
AndroidJUnit4 déprécié
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
Et modifions l'import correspondant.
ContentProvider et mettons en place des tests relatifs à notre base de donnée.
uiautomatorviewer.bat
(dans C:\Users\USER_NAME\AppData\Local\Android\Sdk\tools\bin pour identifier les id des éléments composants l'interface graphique des
applications tierces.
TestSuite nous mettrons en place des tests sur notre application d'envoi de SMS.


and_intent_select_contact pour implémenter la méthode.EditText.EditText
private EditText editTextNumber, editTextText;
Récupérons leur valeur :
editTextNumber = findViewById(R.id.activity_main_editText_number);
editTextText = findViewById(R.id.activity_main_editText_message);
and_send_sms_01_method.
sendSMS(getApplicationContext(), editTextNumber.getText().toString(), editTextText.getText().toString());
and_permission_single.actionToBeCalled(), et
remplaçons le par l'appel à la méthode actionToBeCalled().
java.lang.SecurityException: Sending SMS message: uid XXXXX does not have android.permission.SEND_SMS.
Nous avons :
Manifest.xml.EditText et les Buttons.BroadcastReceivers vers la MainActivity.| Touches | Action |
|---|---|
| Shift deux fois rapidement. | Ouvrir la recherche. |
| F2 | Aller jusqu'à la prochaine erreur. |
| Ctrl + Y | Effacer une ligne. |
| Shift + F6 | Renommer. |
| Shift + Ctrl + Flèche haut/bas | Déplacer une ligne de code. |
| Ctrl + Alt + L | Indenter le code. |
| Alt + F7 | Find usage. |
| Ctrl + Alt + O | Organiser les imports. |
| Ctrl (maintenu) + click souris | Ouvrir la source cliquée. |
| Ctrl + D | Dupliquer la ligne. |

and_tts : pour activer une synthèse vocale.and_intent_voice_recognition : pour activer une reconnaissance vocale.
and_intent_select_contact, et suivre les instructions.
clear.bat, pour effacer les données d'une application :
echo Waiting for device on USB
adb wait-for-usb-device
echo Device found
echo Clear APP Tristan
adb shell pm clear fr.salaun.tristan.android.myapplication
delete.bat, pour effacer une application :
echo Waiting for device on USB
adb wait-for-usb-device
echo Device found
echo "Delete APP Tristan"
adb uninstall fr.salaun.tristan.android.myapplication
install.sh, pour installer plusieurs applications d'un répertoire :
#!/bin/bash
# example : ./install_all_apk.sh /c/apk/
echo Waiting for device on USB
adb wait-for-usb-device
echo Device found
echo "The path to use : $1"
for filename in $1/*.apk; do
echo "installing $filename"
adb install -r -d -g $filename
done
install_apk_all_devices.bat, pour installer une applications sur plusieurs devices :
@echo off
SETLOCAL ENABLEDELAYEDEXPANSION
:: INSTALL ON ALL ATTACHED DEVICES ::
FOR /F "tokens=1,2 skip=1" %%A IN ('adb devices') DO (
start "Sub" install_apk_all_devices_installer.bat %%A
)
ENDLOCAL
:EOF
install_apk_all_devices_installer.bat, qui est appelé par le script précédent :
@echo off
SET ARGUMENTS=%~1
if "%ARGUMENTS%" == "" (
GOTO EOF
)
adb -s %ARGUMENTS% uninstall fr.salaun.tristan.android.myapplication
echo Waking up the device %ARGUMENTS%.
adb -s %ARGUMENTS% shell input keyevent KEYCODE_WAKEUP
REM Sleep for 2 seconds
echo Wait a bit
ping -n 5 127.0.0.1>nul
echo Unlock the screen
adb -s %ARGUMENTS% shell input keyevent 82
echo Install the application.
adb -s %ARGUMENTS% install -r -t D:\apk\debug\fr.salaun.tristan.android.myapplication-debug.apk
if errorlevel 1 goto performdeletebefore
echo Install Success!
goto launch
:performdeletebefore
echo Something bad happened during install.
echo Try to uninstall first.
adb -s %ARGUMENTS% uninstall fr.salaun.tristan.android.myapplication
adb -s %ARGUMENTS% install -r -t D:\apk\debug\fr.salaun.tristan.android.myapplication-debug.apk
if errorlevel 1 goto ERROR
:launch
echo Launch the application.
adb -s %ARGUMENTS% shell am start -n fr.salaun.tristan.android.myapplication/fr.salaun.tristan.android.myapplication.MainActivity
:EOF
exit
:ERROR
echo "Exit with error"
com.example.jsonmyapplication.build.graddle (app) :
implementation 'com.google.code.gson:gson:2.8.6'
Puis créons les classes suivantes :
data class Book(var id: String, var name: String, var author: String, var genre: String, var numpages: Int, var releaseDate: String, var cover: String)
data class BookList (val bookList: List<Book>)
Déclarons une liste de livres :
var list = arrayListOf(
Book(
id = "01fsEF", name = "1984",
author = "George Orwell",
genre = "Fiction dystopique",
numpages = 376,
releaseDate = "1949",
cover = "1984.png"
),
Book(
id = "3H1J0n",
name = "Le Meilleur des mondes",
author = "Aldous Huxley",
genre = "Science-fiction",
numpages = 285,
releaseDate = "1932",
cover = "meilleur-des-mondes.jpg"
),
Book(
id = "MbtsI7",
name = "Malevil",
author = "Robert Merle",
genre = "Littératures de l'imaginaire",
numpages = 541,
releaseDate = "1972",
cover = "malevil.jpg"
)
)
var bookList = BookList (list)
fun jsonToPrettyFormat(jsonString: String?): String? {
val json = JsonParser.parseString(jsonString).asJsonObject
val gson = GsonBuilder()
.serializeNulls()
.disableHtmlEscaping()
.setPrettyPrinting()
.create()
return gson.toJson(json)
}
// Serialization
val gson = Gson()
val listType: Type = object : TypeToken<BookList?>() {}.type
val jsonResult: String = gson.toJson(bookList, listType)
Log.d(TAG, "onCreate jsonResult = $jsonResult")
Log.d(TAG, "onCreate jsonResult = ${jsonToPrettyFormat(jsonResult)}")
Et enfin testons la "Déserialization" :
// Deserialization
val bookList2: BookList = Gson().fromJson(jsonResult, listType)
Log.d(TAG, "onCreate bookList2 = ${bookList2}")
TextView tvDemo = (TextView) findViewById(R.id.textview);
tvDemo.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
copyContentToClipboard("content", ((TextView)v).getText().toString());
return true;
}
});
En Kotlin :
findViewById<TextView>(R.id.textview).setOnLongClickListener(View.OnLongClickListener {
copyContentToClipboard("label", (it as TextView).text.toString())
return@OnLongClickListener true
})
Utilisons le Live Template and_copy_to_clipboard pour écrire la méthode : copyContentToClipboard.
View / Tool Windows / Device File Explorer.
adb exec-out run-as debuggable.app.package.name cat databases/file > file
Plusieurs fichiers
adb exec-out run-as debuggable.app.package.name tar c databases/ > databases.tar
adb exec-out run-as debuggable.app.package.name tar c shared_prefs/ > shared_prefs.tar
A tester :
> adb shell
shell $ run-as com.example.package
shell $ chmod 666 databases/file
shell $ exit ## exit out of 'run-as'
shell $ cp /data/data/package.name/databases/file /sdcard/
shell $ run-as com.example.package
shell $ chmod 600 databases/file
> adb pull /sdcard/file .
adb backup -apk -shared -all -f <filepath>/backup.ab
Restauration :
adb restore <filepath>/backup.ab
Backup d'une seule application (avec APK -apk) :
adb backup -f "“"D:\myfolder\myapp.ab" -apk <package name>
Multiples exemple.
java -jar "C:\ADB\android-backup-tookit\android-backup-extractor\android-backup-extractor-20180203-bin\abe.jar" unpack c:\adb\backup2.ab backup-extracted.tar (You’ll be asked to enter the password)
Toute la documentation.