Talk:Android

From BC$ MobileTV Wiki
Jump to: navigation, search

WebViews

save_html_pdf

        //enable PDF
        webView.getSettings().setBuiltInZoomControls(true);
        webView.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY);
        webView.getSettings().setUseWideViewPort(true);
        webView.getSettings().setLoadWithOverviewMode(true);
        webView.getSettings().setPluginState(WebSettings.PluginState.ON);

http://docs.google.com/viewer?embedded=true&url=


DARK MODE

Android 10 adds a system-wide dark theme, which preserves battery power for devices with OLED screens, reduces eye strain, and facilitates use in low-light environments. These guidelines will show you how to implement a dark theme on Android, even on earlier versions of the platform. 1. Declare dependencies Add the following dependencies to your project:

2. Inherit from a DayNight theme The easiest way to support a dark theme is to inherit from a DayNight theme such as Theme.AppCompat.DayNight. Basically, a DayNight theme is composed of a Light theme in the values directory and a Dark theme in the values-night directory. For example, declare a Theme.MaterialComponents.DayNight.NoActionBar.Bridge: values/themes.xml

values-night/themes.xml

And then, declare your AppTheme: values/themes.xml

values/colors.xml

values-night/colors.xml

3. Use theme attributes for colors When writing your layouts, use theme attributes or night-qualified resources instead of hard-coded colors to ensure that they display suitable colors in a Light theme and in a Dark theme. For example, when you use a FloatingActionButton, the default backgroundTint is ?attr/colorAccent so the tint should be ?android:attr/textColorPrimaryInverse to ensure that the contrast ratio between the icon and its background is eligible:

In a Light theme, it will display a #ffffffff icon on a #ff009688 background.

FloatingActionButton in a light theme In a Dark theme, it will display a #de000000 icon on a #ff80cbc4 background.

FloatingActionButton in a dark theme 4. Allow users to change the app’s theme Your app should let the user switch between themes, which map directly to one of the following modes: Light - AppCompatDelegate.MODE_NIGHT_NO Dark - AppCompatDelegate.MODE_NIGHT_YES System default on Android 10 and higher - AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM Set by Battery Saver on Android 9 or earlier - AppCompatDelegate.MODE_NIGHT_AUTO_BATTERY Use AppCompatDelegate.setDefaultNightMode to switch the theme for all components in your app. Please note that it is not saved when the app is killed so you should use Settings to save the user’s choice. For example, use the following code in your Activity to change the night mode:

And then, use the following code in your Application to restore the night mode:

5. Run your app That’s it, you are ready to run your app and enjoy a dark theme!


For a complete example, check out my sample on GitHub: https://github.com/kfaraj/samples



MediaPlayer

The following snippet is a mostly complete example for audio playback:

package com.oultoncollege.podcatcher;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import android.app.Activity;
import android.content.Intent;
import android.widget.Toast;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * ItemActivity is the individual item detail screen where the item from the broader list which the
 * user selected will have its full details shown.
 *
 * @version 1.0.2
 * @since 2019-04-12
 * @author bcopeland
 */
public class ItemActivity extends Activity implements OnClickListener {
//TODO:                          AppCompatActivity
    private final String TAG = this.getClass().getName();

    private ImageButton btn;
    private MediaPlayer mediaPlayer;

    private SeekBar seekBar;
    private Handler handler;
    private int mediaPos;
    private int mediaMax;

    //remain false until media completes, inside OnCompletionListener make it true
    private boolean intialStage = true;
    private boolean playPause = false;
    private boolean prepared = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_item);
        
        // get references to Widgets
        TextView titleTextView = findViewById(R.id.titleTextView);
        TextView authorTextView = findViewById(R.id.authorTextView);
        TextView pubDateTextView = findViewById(R.id.pubDateTextView);
        TextView descriptionTextView = findViewById(R.id.descriptionTextView);
        TextView linkTextView = findViewById(R.id.linkTextView);
        TextView guidTextView = findViewById(R.id.guidTextView);
        TextView categoryTextView = findViewById(R.id.categoryTextView);
/* DEBUG *
        TextView enclosureTextView = findViewById(R.id.enclosureTextView);
* DEBUG */
        TextView durationTextView = findViewById(R.id.durationTextView);
        ImageView imageTextView = findViewById(R.id.imageView);

        btn = findViewById(R.id.button_play);
        seekBar = (SeekBar) findViewById(R.id.episodeSeekBar);

        ////////////////////////////////////////////////////////////////////////////////////////////
        // get the intent
        Intent intent = getIntent();
//TODO: once you hit 10+ Intent extras, might as well just send whole RSSItem object via Parcelable
        // get data from the intent
        String title = intent.getStringExtra("title");
        String author = intent.getStringExtra("author");
        String pubDate = intent.getStringExtra("pubDate");
        String description = intent.getStringExtra("description").replace('\n', ' ');
        String category = intent.getStringExtra("category");
        String enclosure = intent.getStringExtra("enclosure");
        String duration = intent.getStringExtra("duration");
        String image = intent.getStringExtra("image"); //TODO: cache "feed item art" for performance
        ////////////////////////////////////////////////////////////////////////////////////////////

        // display data on the widgets
        pubDateTextView.setText(pubDate); 
        titleTextView.setText(title);
        authorTextView.setText(author);
        descriptionTextView.setText(description);
        categoryTextView.setText(category);
/* DEBUG *
        enclosureTextView.setText(enclosure); // enclosureTextView.setText(image);
* DEBUG */
//        imageTextView.setImageBitmap(Utils.getBitMapFromImagePath(image));
        durationTextView.setText(duration);

        // set link listener
        linkTextView.setOnClickListener(this);

        // set MediaPlayer listeners
//        initMediaPlayer(enclosure);

        // seek bar listener
//        seekBar.setOnSeekBarChangeListener(seekBarOnSeekChangeListener);
////////////////////////////////////////////////////////////////////////////////////////////////////
        // MEDIA PLAYER start
        mediaPlayer = new MediaPlayer();
        mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);

        handler = new Handler();

        //setup MediaPlayer to stream audio
        try {
            mediaPlayer.setDataSource(enclosure);
            mediaPlayer.prepareAsync();

            // setup Seek Bar duration, max and callbacks
            mediaPos = mediaPlayer.getCurrentPosition();
            mediaMax = mediaPlayer.getDuration();
            seekBar.setMax(mediaMax); // Set the Maximum range of the
            // seekBar.setProgress(mediaPos);// set current stream's progress
//            handler.removeCallbacks(moveSeekBarThread);
//            handler.postDelayed(moveSeekBarThread, 100);

            mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                @Override
                public void onPrepared(MediaPlayer mp) {
                    Log.d(TAG, "Stream buffered, ready to play Audio...");
                    prepared = true;
                }
            });

            mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                @Override
                public void onCompletion(MediaPlayer mp) {
                    intialStage = true;
                    playPause = false;
                    btn.setBackgroundResource(R.drawable.play);
                    mediaPlayer.stop();
                    mediaPlayer.reset();
                    mediaPlayer.release();
                    mediaPlayer = null;
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }

        //play & pause listener
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!prepared) {
                    Toast.makeText(ItemActivity.this, R.string.loading_player, Toast.LENGTH_SHORT).show();
                } else if (prepared && mediaPlayer.isPlaying()) {
                    mediaPlayer.pause();
                    btn.setImageResource(R.drawable.play);
                } else {
                    mediaPlayer.start();
                    btn.setImageResource(R.drawable.pause);
                }
            }
        });

        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                // TODO Auto-generated method stub
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
                // TODO Auto-generated method stub
            }

            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                if (fromUser) {
                    mediaPlayer.seekTo(progress);
                    seekBar.setProgress(progress);
                }
            }
        });
////////////////////////////////////////////////////////////////////////////////////////////////////
    }

    @Override
    public void onClick(View v) {
        // get the intent
        Intent intent = getIntent();
        
        // get the Uri for the link
        String link = intent.getStringExtra("link");
        Uri viewUri = Uri.parse(link);
        
        // create the intent and start it
        Intent viewIntent = new Intent(Intent.ACTION_VIEW, viewUri); 
        startActivity(viewIntent);
    }

    /**
     * initialize the MediaPlayer
     */
    public void initMediaPlayer(String enclosure) {
        // MEDIA PLAYER start
        mediaPlayer = new MediaPlayer();
        mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);

        handler = new Handler();

        //setup MediaPlayer to stream audio
        try {
            mediaPlayer.setDataSource(enclosure);
            mediaPlayer.prepareAsync();

            // setup Seek Bar duration, max and callbacks
            mediaPos = mediaPlayer.getCurrentPosition();
            mediaMax = mediaPlayer.getDuration();
            seekBar.setMax(mediaMax); // Set the Maximum range of the
            // seekBar.setProgress(mediaPos);// set current stream's progress
//            handler.removeCallbacks(moveSeekBarThread);
//            handler.postDelayed(moveSeekBarThread, 100);

            mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                @Override
                public void onPrepared(MediaPlayer mp) {
                    Log.d(TAG, "Stream buffered, ready to play Audio...");
                    prepared = true;
                }
            });

            mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                @Override
                public void onCompletion(MediaPlayer mp) {
                    intialStage = true;
                    playPause = false;
                    btn.setBackgroundResource(R.drawable.play);
                    mediaPlayer.stop();
                    mediaPlayer.reset();
                    mediaPlayer.release();
                    mediaPlayer = null;
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }

        //play & pause listener
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!prepared) {
                    Toast.makeText(ItemActivity.this, R.string.loading_player, Toast.LENGTH_SHORT).show();
                } else if (prepared && mediaPlayer.isPlaying()) {
                    mediaPlayer.pause();
                    btn.setImageResource(R.drawable.play);
                } else {
                    mediaPlayer.start();
                    btn.setImageResource(R.drawable.pause);
                }
            }
        });
    }


    /**
     * Seek Bar controller
     *
    private Runnable moveSeekBarThread = new Runnable() {
        public void run() {
            if (mediaPlayer == null) {
                Log.e(TAG, "Tried to register Seek Bar listener thread while MediaPlayer not fully initialized...");
            } else if (mediaPlayer.isPlaying()) {
                int mediaPosition = mediaPlayer.getCurrentPosition();
                int mediaMax = mediaPlayer.getDuration();

                seekBar.setMax(mediaMax);
                seekBar.setProgress(mediaPosition);

                handler.postDelayed(this, 100); // Looping the thread after 0.1 second
            }
        }
    };
     */

    /**
     * Seek Bar's manual (user) position change listener
     */
    SeekBar.OnSeekBarChangeListener seekBarOnSeekChangeListener = new SeekBar.OnSeekBarChangeListener() {
        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {
            // TODO Auto-generated method stub
        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {
            // TODO Auto-generated method stub
        }

        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            if (fromUser) {
                mediaPlayer.seekTo(progress);
                seekBar.setProgress(progress);
            }
        }
    };

}